; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
        SUBT    Extra long integer manipulation macros => &.Hdr.ExtraLong

OldOpt  SETA    {OPT}
        OPT     OptNoList+OptNoP1List

; ***********************************
; ***    C h a n g e   L i s t    ***
; ***********************************

; Date       Name  Description
; ----       ----  -----------
; 26-Sep-89  JSR   Created from macros used in ColourTrans
; 27-Sep-89  JSR   Add end statement to end of file
; 10-Feb-93  BC    Restored OPT correctly


;************************************************************
;
;       The following macros perform various operations on
;       extralong integers (64 bit long).
;
;************************************************************

;
; The integers exist in register pairs, both registers have
; to be specified to the relevant macro.
;

;*****************************************************************
;***                                                           ***
;***                      rc = ra+rb                           ***
;***                                                           ***
;*****************************************************************
;
; ra and rb preserved
; no restriction on which registers may be used
; condition codes corrupted
;
        MACRO
        mextralong_add  $rcl, $rch, $ral, $rah, $rbl, $rbh
        ADDS    $rcl, $ral, $rbl
        ADC     $rch, $rah, $rbh
        MEND


;*****************************************************************
;***                                                           ***
;***                      rc = ra-rb                           ***
;***                                                           ***
;*****************************************************************
;
; ra and rb preserved
; no restriction on which registers may be used
; condition codes corrupted
;
        MACRO
        mextralong_subtract     $rcl, $rch, $ral, $rah, $rbl, $rbh
        SUBS    $rcl, $ral, $rbl
        SBC     $rch, $rah, $rbh
        MEND


;*****************************************************************
;***                                                           ***
;***                      rc = ra*rb                           ***
;***                                                           ***
;*****************************************************************
;
; ra and rb preserved
; ra, rb and rc must be different register pairs
; condition codes corrupted
;
        MACRO
        mextralong_multiply     $rcl, $rch, $ral, $rah, $rbl, $rbh
        STMFD   r13!, {$ral, $rah, $rbl, $rbh}

        ; result is 0
        MOV     $rcl, #0
        MOV     $rch, #0

        ; enter the main loop
        B %FT02

03
        ; a = a>>1 (unsigned) with remainder in C
        MOVS    $rah, $rah, LSR #1
        MOVS    $ral, $ral, RRX

        ; no remainder: skip next sequence
        BCC     %FT01

        ; result += b
        ADDS    $rcl, $rcl, $rbl
        ADC     $rch, $rch, $rbh

01
        ; b *= 2
        ADDS    $rbl, $rbl, $rbl
        ADC     $rbh, $rbh, $rbh

02
        ; if ra is non-zero restart the loop
        CMP     $rah, #0
        CMPEQ   $ral, #0

        BNE     %BT03

        LDMFD   r13!, {$ral, $rah, $rbl, $rbh}
        MEND


;*****************************************************************
;***                                                           ***
;***                      rc = ra/rb (signed)                  ***
;***                                                           ***
;*****************************************************************
;
; ra and rb preserved, rw and rs corrupted
; ra, rb, rc, rw must be different register pairs
; rw and rs are working registers.
; condition codes corrupted
;
        MACRO
        mextralong_divide       $rcl, $rch, $ral, $rah, $rbl, $rbh, $rwl, $rwh, $rs
        STMFD   r13!, {$ral, $rbl, $rah, $rbh}

        ; Mask the sign bit into $rs, setting the CCs
        ANDS    $rs, $rah, #&80000000
        BEQ     %FT05
        ; If negative change the sign of ra by ra=0-ra
        RSBS    $ral, $ral, #0
        RSC     $rah, $rah, #0
05

        ; XOR the two sign bits of $rs and rb.
        ; The ASR #32 (encoded as ASR #0) gets $rbh's sign into the carry flag
        EORS    $rs, $rs, $rbh, ASR #32
        BCC     %FT06
        ; If negative change its sign
        RSBS    $rbl, $rbl, #0
        RSC     $rbh, $rbh, #0
06

        ; $rs's sign bit contains the xor of ra's and rb's sign bits
        ; ra and rb are now guaranteed to be positive.

        ; MOV rt, rb
        MOV     $rwl, $rbl
        MOV     $rwh, $rbh

01
        ; ADD rc, rt, rt
        ADDS    $rcl, $rwl, $rwl
        ADC     $rch, $rwh, $rwh

        ; CMP rc, ra
        CMP     $rch, $rah
        CMPEQ   $rcl, $ral

        ; MOVLS rt, rc
        MOVLS   $rwl, $rcl
        MOVLS   $rwh, $rch

        BLS %BT01

        ; MOV rc, #0
        MOV     $rcl, #0
        MOV     $rch, #0

02
        ; CMP ra, rt
        CMP     $rah, $rwh
        CMPEQ   $ral, $rwl

        ; The following 'inefficient' structure for doing
        ; The subtract/accumulate is required due to the
        ; ADC rc, rc, rc altering the C flag.

        BCC     %FT03

        ; ADC rc, rc, rc
        ADCS    $rcl, $rcl, $rcl
        ADC     $rch, $rch, $rch

        ; SUB ra, ra, rt
        SUBS    $ral, $ral, $rwl
        SBC     $rah, $rah, $rwh

        B %FT04

03
        ; ADD rc, rc, rc
        ADDS    $rcl, $rcl, $rcl
        ADC     $rch, $rch, $rch

04
        ; MOV rt, rt, LSR #1
        MOVS    $rwh, $rwh, LSR #1
        MOV     $rwl, $rwl, RRX

        ; CMP rt, rb
        CMP     $rwh, $rbh
        CMPEQ   $rwl, $rbl

        BCS     %BT02

        ; Store result and return if sign is correct
        TST     $rs, #&80000000
        BEQ     %FT07

        ; Must need sign changing to negative, so lets do it
        RSBS    $rcl, $rcl, #0
        RSC     $rch, $rch, #0

07
        LDMFD   r13!, {$ral, $rbl, $rah, $rbh}
        MEND



;*****************************************************************
;***                                                           ***
;***                      rd = rs << shift (signed)            ***
;***                                                           ***
;*****************************************************************
; rs preserved
; shift is an integer constant
; condition codes preserved
; rd must not be rs
; for negative shifts the shift is arithmetic
;
        MACRO
        mextralong_leftshift    $rdl, $rdh, $rsl, $rsh, $shift
    [ $shift < &80000000
     [ $shift < 32
        ; 0 <= $shift < 32
        MOV     $rdh, $rsh, ASL #$shift
        ORR     $rdh, $rdh, $rsl, LSR #32-$shift
        MOV     $rdl, $rsl, ASL #$shift
     |
      [ $shift = 32
        ; shift = 32
        MOV     $rdh, $rsl
        MOV     $rdl, #0
      |
        ; shift > 32
        MOV     $rdh, $rsl, ASL #$shift-32
        MOV     $rdl, #0
      ]
     ]
    |
     [ $shift > -32
        ; -32 < shift < 0
        MOV     $rdl, $rsl, LSR #-$shift
        ORR     $rdl, $rdl, $rsh, ASL #32+$shift
        MOV     $rdh, $rsh, ASR #-$shift
     |
      [ $shift = -32
        ; shift = -32
        MOV     $rdl, $rsh
        MOV     $rdh, $rsh, ASR #31
      |
        ; shift < -32
        MOV     $rdl, $rsh, ASR #64+$shift
        MOV     $rdh, $rsh, ASR #31
      ]
     ]
    ]

        MEND


;*****************************************************************
;***                                                           ***
;***         sign extend a integer into an extralong           ***
;***                                                           ***
;*****************************************************************
;
;       rl conatins the integer
;       (rl,rh) is the resultant extralong
;
        MACRO
        mextralong_sex  $rl, $rh
        MOV     $rh, $rl, ASR #31
        MEND


;*****************************************************************
;***                                                           ***
;***            Move an extralong between registers            ***
;***                                                           ***
;*****************************************************************
;
; MOV rd, rs
;
        MACRO
        mextralong_mov  $rdl, $rdh, $rsl, $rsh
        MOV     $rdl, $rsl
        MOV     $rdh, $rsh
        MEND

        OPT     OldOpt

        END
