/*******************************************************************************
  Big Integer Assembly Helpers

  Summary:
    Library for Microchip TCP/IP Stack
    
  Description:
    - Accelerates processing for BigInt functions
*******************************************************************************/

/*******************************************************************************
FileName:   BigInt_helper_C32.S
Copyright © 2012 released Microchip Technology Inc.  All rights
reserved.

Microchip licenses to you the right to use, modify, copy and distribute
Software only when embedded on a Microchip microcontroller or digital signal
controller that is integrated into your product or third party product
(pursuant to the sublicense terms in the accompanying license agreement).

You should refer to the license agreement accompanying this Software for
additional information regarding your rights and obligations.

SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER
CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR
OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR
CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF
SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
(INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
*******************************************************************************/

#include <p32xxxx.h> 

#define  ONE_VALUE                  0x00000001

#define  ZERO_VALUE                 0x00000000

.bss
.align    4 
.global   _iA
.global   _xA
.global   _iB
.global   _xB
.global   _iR
.global   _wC
_iA:          .word         4   /* _iA, starting index for A (lower memory address, least significant byte/word) */
_xA:          .word         4   /* _xA, end index for A (higher memory address, most significant byte/word) */
_iB:          .word         4   /* _iB, starting index for B (lower memory address, least significant byte/word) */
_xB:          .word         4   /* _xB, end index for B (higher memory address, most significant byte/word) */
_iR:          .word         4   /* _iR, starting index for Res (lower memory address, least significant byte/word) */
_wC:          .word         4   /*  value of B for _mas (little endian word) */

 .set noreorder 

.text

/***************************************************************************
; Function: 	void _addBI()
;
; PreCondition: _iA and _iB are loaded with the address of the LSB of the BigInt
;				_xA and _xB are loaded with the address of the MSB of the BigInt
;				a.size >= b.magnitude
;
; Input: 		A and B, the BigInts to add
;
; Output: 		A = A + B
;
; Side Effects: None
;
; Overview: 	Quickly performs the bulk addition of two BigInts
;
; Note:			Function works
;***************************************************************************/

/* Note :- There is no Carry/Borrow Flag in STATUS Register of PIC32...So Workarounds were done */

    .global	_addBI

 .ent _addBI

_addBI:
    la     t0, _xA            # Load adress of _xA
    lw     t0, 0(t0)          # Load the Contents of memory pointing by t0 register
    
    la     t1, _iA
    addiu  t0, t0, 4          # Store the address present in _xA & increment it by 4 ( since every access is 32 bit )
    lw     t1, 0(t1)          # Store the address present in __iA
    
    la     t2, _xB
    lw     t2, 0(t2)

    la     t3, _iB
    addiu  t2, t2, 4          # Store the address present in _xB & increment it by 4
    lw     t3, 0(t3)          # Store the address present in _iB

    la    t6, ZERO_VALUE      # Store 0x00000000 in t6

aDoAdd:
    lw     t4, 0(t3)          # Place the LSW of _iB in t4
    addiu  t3, t3, 4          # t3 = t3 + t4
    lw     t5, 0(t1)          # Place the LSW of _iA in t5
    addu   t5, t5, t6         # t5 = t5 + t6
    addu   t7, t5, t4         # t7 = t5 + t4
    sw     t7, 0(t1)          # 4 bytes of data transfered from address specified by t1 to t5
    sltu   t8, t7, t5         # if ( t7 < t5 ) then ( t8 = 1 )
    movz   t8, t6, t5
    move   t6, t8             # PrevCarry = carry
    bne    t3, t2, aDoAdd     # if t3 != t2 then Jump to 'aDoAdd'
    addiu  t1, t1, 4
    beqz   t6, addDone
    nop

AddCarry:
    beq    t0, t1, addDone      # if t0 = t1 then Jump to 'nextDoadd'
    lw     t7, 0(t1)
    addiu  t5, t7, 1
    sw     t5, 0(t1)          # Store the Contents of t5 in memory pointed by t1 register
    addiu  t1, t1, 4
    beqz   t7, AddCarry
    nop

addDone:
    jr     ra                # return to function from where you are called
    nop

 .end _addBI


/***************************************************************************
; Function: 	void _subBI()
;
; PreCondition: _iA and _iB are loaded with the address of the LSB of the BigInt
;				_xA and _xB are loaded with the address of the MSB of the BigInt
;
; Input: 		A and B, the BigInts to subtract
;
; Output: 		A = A - B
;
; Side Effects: None
;
; Overview: 	Quickly performs the bulk subtraction of two BigInts
;
; Note:			Function works
;***************************************************************************/
    .global _subBI

 .ent _subBI

_subBI:
    la     t0, _xA
    lw     t0, 0(t0)           # Store the address present in __xA & increment it by 4
    
    la     t1, _iA            # Store the address present in __iA
    addiu  t0, t0, 4
    lw     t1, 0(t1)
    
    la     t2, _xB            # Store the address present in __xB & increment it by 4
    lw     t2, 0(t2)

    la     t3, _iB            # Store the address present in __iB
    addiu  t2, t2, 4
    lw     t3, 0(t3)

    la     t6, ZERO_VALUE      # Store 0x00000000 in t6

sDoSub:
    move   t8,$0
    lw     t4, 0(t3)          # Place the LSW of __iB in t4
    addiu  t3, t3, 4          # t3 = t3 + t4
    lw     t5, 0(t1)          # Place the LSW of __iA in t5
    movz   t8, t6, t5
    subu   t5, t5, t6         # t8 = t5 - t6
    sltu   t7, t5, t4         # if ( t8 < t4 ) then ( t7 = 1 )
    subu   t5, t5, t4         # t5 = t5 - t4
    sw     t5, 0(t1)          # 4 bytes of data transfered from address specified by t1 to t5
    or     t7, t7, t8
    move   t6, t7
    bne    t3, t2, sDoSub
    addiu  t1, t1, 4
    beqz   t6, subDone
    nop

SubBorrow:
    beq    t0, t1, subDone
    lw     t7, 0(t1)
    addiu  t5, t7, -1
    sw     t5, 0(t1)
    addiu  t1, t1, 4
    beq    t7,$0, SubBorrow
    nop

subDone:
    jr     ra
    nop

 .end _subBI


/***************************************************************************
; Function: 	void _zeroBI()
;
; PreCondition: _iA is loaded with the address of the LSB of the BigInt
;				_xA is loaded with the address of the MSB of the BigInt
;
; Input: 		None
;
; Output: 		A = 0
;
; Side Effects: None
;
; Overview: 	Sets all words from _iA to _xA to zero
;
; Note:			Function works
;***************************************************************************/
    .global _zeroBI

 .ent _zeroBI

_zeroBI:
    
    la     t0, _xA
    lw     t0, 0(t0)           # Store the address present in __xA & increment it by 4
    
    la     t1, _iA            # Store the address present in __iA
    addiu  t0, t0, 4
    lw     t1, 0(t1)

zDoZero:
    sw     $0, 0(t1)          # The value of $0 register is always 0
    addiu  t1, t1, 4
    bne    t0, t1, zDoZero
    nop
    jr      ra
    nop

 .end _zeroBI


/***************************************************************************
; Function: 	void _msbBI()
;
; PreCondition: _iA is loaded with the address of the LSB of the BigInt buffer
;				_xA is loaded with the address of the right most byte of the BigInt buffer
;
; Input: 		None
;
; Output: 		_xA is now pointing to the MSB of the BigInt
;
; Side Effects: None
;
; Overview: 	Finds the MSB (first non-zero word) of the BigInt, starting 
;				from the right-most word and testing to the left.  This 
;				function will stop if _iA is reached.
;
; Note:			Function works
;***************************************************************************/
    .global _msbBI

 .ent _msbBI

_msbBI:
    
    la     t2, _xA
    lw     t0, 0(t2)           # Store the address present in __xA & increment it by 4

    la     t1, _iA            # Store the address present in __iA
    lw     t1, 0(t1)

msbLoop:
    lw     t3, 0(t0)
    bne    t3, $0, msbDone
    nop
    addiu  t0, t0, -4
    bne    t0, t1, msbLoop
    nop

msbDone:
    sw     t0, 0(t2)
    jr     ra
    nop

 .end _msbBI


/***************************************************************************
; Function: 	void _mulBI()
;
; PreCondition: _iA and _iB are loaded with the address of the LSB of each BigInt
;				_xA and _xB are loaded with the address of the MSB of the BigInt
;				_iR is loaded with the LSB address of the destination result memory
;				_iR memory must be zeroed and have enough space (_xB-_iB+_xA-_iA words)
;
; Input: 		A and B, the BigInts to multiply
;
; Output: 		R = A * B
;
; Side Effects: None
;
; Overview: 	Performs the bulk multiplication of two BigInts
;
; Note:			Function works. An Assumption has been made that 'R' is cleared to
;               'Zero' before calling this function.
;***************************************************************************/

    .global _mulBI

 .ent _mulBI

_mulBI:

    la     t0, _xA
    lw     t0, 0(t0)           # Store the address present in _xA & increment it by 4
    
    la     t1, _iA            # Store the address present in _iA
    addiu  t0, t0, 4
    lw     t1, 0(t1)
    
    la     t2, _xB            # Store the address present in _xB & increment it by 4
    lw     t2, 0(t2)

    la     t3, _iB            # Store the address present in _iB
    addiu  t2, t2, 4
    lw     t3, 0(t3)

    la     t4, _iR            # Store the address present in _iR
    lw     t4, 0(t4)

    la     a0, ONE_VALUE                    # Store 0x00000001 in a0

    addiu  t6, $0, 0                        # Store 0x0 in t6

mLoopB:
    beq  t3, t2, mDone
    lw   t7, 0(t3)

    beq  t7, $0, mContinue
    addu t8, t4, t6
    
    mthi  $0           # Move 0 to HI Register.

    addiu t5, t1, 0

mLoopA:
    mfhi  a1           # Store the contents from HI Register to 'a1' register
    lw    a3,0(t8)
    addu  a2, a1, a3
    mtlo  a2           # Move the contents from 'a1' register to LO register
    sltu  a1, a2, a1
    bgtz  a1, mHiPutOne
    nop
    b     mMultAdd
    mthi  $0

mHiPutOne:
    mthi  a0           # Move the contents from 'a0' register to HI register

mMultAdd:
    lw    a1, 0(t5)
    maddu a1, t7       # Multiply a1 with t7 then add with HI || LO register ( i.e HI || LO + a1 * t7 )
    addiu t5, t5, 4
    mflo  a3           # Store the contents from LO Register to 'a3' register
    sw    a3,0(t8)
    addiu t8, t8, 4
    bne   t5,t0, mLoopA
    nop
    mfhi  a1           # Store the contents from HI Register to 'a1' register
    sw    a1, 0(t8)    # Move the contents from 'a1' register to LO register

mContinue:
    addiu t3, t3, 4
    b     mLoopB
    addiu t6, t6, 4

mDone:
    jr      ra
    nop

 .end _mulBI


/***************************************************************************
; Function: 	void _sqrBI()
;
; PreCondition: _iA is loaded with the address of the LSB of the BigInt
;				_xA is loaded with the address of the MSB of the BigInt
;				_iR is loaded with the LSB address of the destination result memory
;				_iR memory must be zeroed and have enough space (_xB-_iB+_xA-_iA words)
;
; Input: 		A, the BigInt to square
;
; Output: 		R = A * A
;
; Side Effects: None
;
; Overview: 	Squares BigInt A and stores result in R
;
; Note:			Function works
;***************************************************************************/
	.global	_sqrBI

 .ent _sqrBI

_sqrBI:

    la     t0, _xA
    lw     t0, 0(t0)           # Store the address present in _xA & increment it by 4
    
    la     t1, _xB            # Store the address present in _xB & increment it by 4
    sw     t0, 0(t1)

    la     t0, _iA            # Store the address present in _iA
    lw     t0, 0(t0)
    
    la     t1, _iB            # Store the address present in _iB

    b      _mulBI
    sw     t0, 0(t1)

 .end _sqrBI

/***************************************************************************
; Function: 	void _masBI()
;
; PreCondition: _iB is loaded with the LSB of the modulus BigInt
;				_xB is loaded with the MSB of the modulus BigInt
;				_wC is loaded with the 32 bit unsigned integer by which to multiply
;				_iR is the starting LSB of the decumulator BigInt
 .end __sqrBI
;
; Input: 		B (BigInt) and C (16-bit int) to multiply
;
; Output: 		R = R - (B * C)
;
; Side Effects: None
;
; Overview: 	Performs a Multiply And Subtract function.  This is used in
;				the modulus calculation to save several steps.  A BigInt (iB/xB)
;				is multiplied by a single word and subtracted rather than
;				accumulated.
;
; Note:			Decumulator is the opposite of an accumulator,
;				if that wasn't obvious
;
; Note:			Function works
;***************************************************************************/
    .global _masBI

 .ent _masBI

_masBI:

    la     t2, _xB            # Store the address present in _xB & increment it by 4
    lw     t2, 0(t2)

    la     t3, _iB            # Store the address present in _iB
    addiu  t2, t2, 4
    lw     t3, 0(t3)

    la     t1, _wC            # Store the address present in _wC
    lw     t1, 0(t1)

    la     t4, _iR            # Store the address present in _iR
    lw     t4, 0(t4)

    la     a1, ZERO_VALUE
    
    subu   t8, t2, t3
    addiu  t8, t8, 4
    addiu  t9, t4, 0
    addiu  a0, $0, 1

masLoop:
    
    mthi   $0
    mfhi   t6

mMultandSub:
    mtlo   t6
    mthi   $0
    lw     t5, 0(t3)
    maddu  t5, t1
    nop
    mflo   t6
    
mCalc:
    move   a2, $0
    lw     t5, 0(t4)
    movz   a2, a1, t5
    subu   t5, t5, a1
    sltu   t7, t5, t6         # if ( t5 < t6 ) then ( t7 = 1 )
    subu   t5, t5, t6         # t5 = t5 - t6
    sw     t5, 0(t4)          # 4 bytes of data transfered from address specified by t4 to t5
    or     t7, t7, a2
    move   a1, t7
    addiu  t4, t4, 4
    addiu  t3, t3, 4
    bne    t3, t2, mMultandSub
    mfhi   t6
    move   a2, $0
    lw     t5, 0(t4)
    movz   a2, a1, t5
    subu   t5, t5, a1
    sltu   t7, t5, t6         # if ( t5 < t6 ) then ( t7 = 1 )
    subu   t5, t5, t6         # t5 = t5 - t6
    sw     t5, 0(t4)          # 4 bytes of data transfered from address specified by t4 to t5
    or     t7, t7, a2
    move   a1, t7
    addiu  t4, t4, 4
    beqz   a1, masDone

mSubBorrow:
    subu   t7, t4, t9
    beq    t7, t8, masDone
    lw     t7, 0(t4)
    addiu  t6, t7, -1
    sw     t6, 0(t4)
    addiu  t4, t4, 4
    beq    t7, $0, mSubBorrow
    nop

masDone:
    jr     ra
    nop
 
 .end _masBI


/***************************************************************************
; Function: 	void _copyBI()
;
; PreCondition: _iA and _iB are loaded with the address of the LSB of each BigInt
;				_xA and _xB are loaded with the address of the MSB of each BigInt
;
; Input: 		A and B, the destination and source
;
; Output: 		A = B
;
; Side Effects: None
;
; Stack Req: 	
;
; Overview: 	Copies a value from one BigInt to another
;
; Note:			Function works
;***************************************************************************/
    .global	_copyBI

 .ent _copyBI

_copyBI:

    la     t0, _xA
    lw     t0, 0(t0)           # Store the address present in __xA & increment it by 4
    
    la     t1, _iA            # Store the address present in __iA
    addiu  t0, t0, 4
    lw     t1, 0(t1)
    
    la     t2, _xB            # Store the address present in __xB & increment it by 4
    lw     t2, 0(t2)

    la     t3, _iB            # Store the address present in __iB
    addiu  t2, t2, 4
    lw     t3, 0(t3)

cLoop:
    lw     t4, 0(t3)
    sw     t4, 0(t1)
    addiu  t3, t3, 4
    beq    t3, t2, cZeroLoopTest
    addiu  t1, t1, 4
    bne    t1,t0, cLoop
    nop
    jr      ra
    nop

cZeroLoop:
    sw     $0, 0(t1)
    addiu  t1, t1, 4

cZeroLoopTest:
    bne    t1,t0, cZeroLoop
    nop

cend:
    jr      ra
    nop
 
 .end _copyBI

 .set reorder

