/*
 * Copyright (c) 1995 FirePower Systems, Inc.
 * DO NOT DISTRIBUTE without permission
 *
 * $RCSfile: pxmisc.s $
 * $Revision: 1.7 $
 * $Date: 1996/01/11 07:11:46 $
 * $Locker:  $
 */

//++
//
// Copyright (c) 1993  IBM Corporation
//
// Copyright (c) 1994 MOTOROLA, INC.  All Rights Reserved.  This file
// contains copyrighted material.  Use of this file is restricted
// by the provisions of a Motorola Software License Agreement.
//
// Module Name:
//
//    pxmisc.s
//
// Abstract:
//
//    This module implements miscellaneous routines on the PowerPC.
//
// Author:
//
//    Steve Johns (sjohns@pets.sps.mot.com) August 1994
//
// Environment:
//
//    Kernel mode only.
//

#include "kxppc.h"

        .set	HID0, 1008		// SPR # for HID0


	LEAF_ENTRY(HalpGetHID0)

 	mfspr	r.3, HID0

	LEAF_EXIT(HalpGetHID0)
   



	LEAF_ENTRY(HalpSetHID0)

	mtspr	HID0, r.3

	LEAF_EXIT(HalpSetHID0)


        LEAF_ENTRY(HalpSetDecrementer)

        mtdec   r.3			// Set the DECREMENTER

        LEAF_EXIT(HalpSetDecrementer)




//
// HalpLockDisplayString
//
// This routine implements a lock on the HalDisplayString function
// to avoid garbling the display when more than one processor attempts
// to display a string at the same time.
//
// r.3 is the address of the lock.
//

        LEAF_ENTRY(HalpLockDisplayString)

        li      r.5, 1
        lis     r.6, 0x99
LkDis:  lwarx   r.4, 0, r.3
        addic.  r.6, r.6, -1
        beq     LkSync
        cmpwi   r.4, 0
        bne-    LkDis                   // jif lock already held
        stwcx.  r.5, 0, r.3             // attempt to take lock
        sync
        beqlr+                          // return if all was good

//
// We lost the reserve, allow the other processor a chance to
// win this argument.
//

        sync
        sync
        sync
        sync
        sync
        sync
        sync
        sync
        sync
        sync
        sync
        sync
        b       LkDis

LkSync:
        stw     r.5, 0(r.3)             // Grab the lock anyway
        LEAF_EXIT(HalpLockDisplayString)



// ULONG  HalpDivide (
//    IN ULARGE_INTEGER Dividend,
//    IN ULONG Divisor)
//
// Routine Description:
//
//    This function divides an unsigned large integer by an unsigned long
//    and returns the resultant quotient and optionally the remainder.
//
//    N.B. It is assumed that no overflow will occur.
//
// Arguments:
//
//    Dividend (r.3, r.4) - Supplies the dividend value.
//       (High-order bits in r.4, low-order bits in r.3)
//
//    Divisor (r.5) - Supplies the divisor value.
//
// Return Value:
//
//    The ULONG quotient is returned as the function value.
//
//--



	.set	Quotient, r.3
	.set	DividendLo, r.3
	.set	DividendHi, r.4
	.set	Divisor, r.5
	.set	Q1, r.11
	.set	N, r.12
	.set	Q0, N			// Use of N & Q0 don't overlap

	LEAF_ENTRY(HalpDivide)


	cmplw   DividendHi,Divisor
	bge     overflow		// catch overflow or division by 0
	cmplwi  DividendHi,0		// test high part for 0
	bne	Divide64Bits

// High 32 bits of Dividend == 0, so use 32 bit division.
	divwu   DividendLo,DividendLo,Divisor	// result <- dividend / divisor
	blr

Divide64Bits:

// Normalize:  Shift divisor and dividend left to get rid of leading zeroes
// in the divisor.  Since DividendHi < Divisor, only zeroes are shifted out
// of the dividend.
	cntlzw  N,Divisor		// number of bits to shift (N)
	slw     Divisor,Divisor,N	// shift divisor
	slw     DividendHi,DividendHi,N // shift upper part of divisor
	mr	r.8, DividendLo		// Save unshifted DividendLo
	slw     DividendLo,DividendLo,N // shift lower part of divisor
	subfic  N,N,32			// 32-N
	srw     N,r.8,N			// leftmost N bits of DividendLo, slid right
	or      DividendHi,DividendHi,N	 //   and inserted into low end of DividendHi

// Estimate high-order halfword of quotient.  If the dividend is
// A0 A1 A2 A3 and the divisor is B0 B1  (where each Ai or Bi is a halfword),
// then the estimate is A0 A1 0000 divided by B0 0000, or A0 A1 divided by B0.
// (DividendHi holds A0 A1, DividendLo holds A2 A3, and Divisor holds B0 B1.)
// The estimate may be too high because it does not account for B1; in rare
// cases, the estimate will not even fit in a halfword.  High estimates are
// corrected for later.
	srwi    r.8,Divisor,16		// r.8 <- B0
	divwu   Q0,DividendHi,r.8	// Q0 <- floor([A0 A1]/B0)
// Subtract partial quotient times divisor from dividend: If Q0 is the quotient
// computed above, this means that Q0 0000 times B0 B1 is subtracted from
// A0 A1 A2 A3.  We compute Q0 times B0 B1 and then shift the two-word
// product left 16 bits.
	mullw   r.9,Q0,Divisor		// low word of Q0 times B0 B1
	mulhwu  r.10,Q0,Divisor		// high word of Q0 times B0 B1
	slwi    r.10,r.10,16		// shift high word left 16 bits
	inslwi  r.10,r.9,16,16		// move 16 bits from left of low word
				  	//   to right of high word
	slwi    r.9,r.9,16		// shift low word left 16 bits
	subfc   DividendLo,r.9,DividendLo	// low word of difference
	subfe   DividendHi,r.10,DividendHi	// high word of difference
// If the estimate for Q0 was too high, the difference will be negative.
// While A0 A1 A2 A3 is negative, repeatedly add B0 B1 0000 to A0 A1 A2 A3
// and decrement Q0 by one to correct for the overestimate.
	cmpwi   DividendHi,0		// A0 A1 A2 A3 is negative iff A0 A1 is
	bge     Q0_okay			// no correction needed
	inslwi  r.10,Divisor,16,16	// high word of B0 B1 0000 (= 0000 B0)
	slwi    r.9,Divisor,16		// low word of B0 B1 0000 (= B1 0000)
adjust_Q0:
	addc   DividendLo,DividendLo,r.9 // add B0 B1 0000 to A0 A1 A2 A3 (low)
	adde   DividendHi,DividendHi,r.10 // add B0 B1 0000 to A0 A1 A2 A3 (high)
	cmpwi  DividendHi,0		// Is A0 A1 A2 A3 now nonnegative?
	addi   Q0,Q0,-1			// decrement Q0
	blt    adjust_Q0	  	// if A0 A1 A2 A3 still negative, repeat
Q0_okay:
// Estimate low-order halfword of quotient.  A0 is necessarily 0000 at this
// point, so if the remaining part of the dividend is A0 A1 A2 A3 then the
// estimate is A1 A2 0000 divided by B0 0000, or A1 A2 divided by B0.
// (DividendHi holds A0 A1, DividendLo holds A2 A3, and r.8 holds B0.)
	slwi    r.9,DividendHi,16	// r.9 <- A1 0000
	inslwi  r.9,DividendLo,16,16	// r.9 <- A1 A2
	divwu   Q1,r.9,r.8		// Q1 <- floor([A1 A2]/B0)
// Subtract partial quotient times divisor from remaining part of dividend:
// If Q1 is the quotient computed above, this means
// that Q1 times B0 B1 is subtracted from A0 A1 A2 A3.  We compute
	mullw   r.9,Q1,Divisor		// low word of Q1 times B0 B1
	mulhwu  r.10,Q1,Divisor		// high word of Q1 times B0 B1
	subfc   DividendLo,r.9,DividendLo	// low word of difference
	subfe   DividendHi,r.10,DividendHi	// high word of difference
// If the estimate for Q1 was too high, the difference will be negative.
// While A0 A1 A2 A3 is negative, repeatedly add B0 B1 to A0 A1 A2 A3
// and decrement Q1 by one to correct for the overestimate.
	cmpwi   DividendHi,0		// A0 A1 A2 A3 is negative iff A0 A1 is
	bge     Q1_okay			// no correction needed
adjust_Q1:
	addc   DividendLo,DividendLo,Divisor	// add B0 B1 to A0 A1 A2 A3 (low)
	addze  DividendHi,DividendHi	// add B0 B1 to A0 A1 A2 A3 (high)
	cmpwi  DividendHi,0		// Is A0 A1 A2 A3 now nonnegative?
	addi   Q1,Q1,-1			// decrement Q1
	blt    adjust_Q1	  	// if A0 A1 A2 A3 still negative, repeat
Q1_okay:
	slwi   Quotient,Q0,16		// Quotient <- Q0 A1
	or     Quotient,Quotient,Q1
	blr


// The error cases:
overflow:
	li	Quotient, 0		// return(0);
	LEAF_EXIT(HalpDivide)




// ULARGE_INTEGER  HalpMultiply (
//    IN ULONG Multiplicand,
//    IN ULONG Multiplier)
//
// Routine Description:
//
//    This function multiplies two ULONGS and returns a ULARGE_INTEGER product.
//
//    N.B. It is assumed that no overflow will occur.
//
// Arguments:
//
//    Multiplicand (r.4) - Supplies the multiplicand value.
//
//    Multiplier (r.5) - Supplies the multiplier value.
//
// Return Value:
//
//    The ULARGE_INTEGER product is returned as the function value.
//

	.set	Multiplicand, r.4
	.set	Multiplier, r.5
	.set	Product, r.3


	LEAF_ENTRY(HalpMultiply)

	mullw	r.0, Multiplicand, Multiplier	// Calculate low 32 bits
	stw	r.0, 0(Product)
	mulhwu	r.0, Multiplicand, Multiplier	// Calculate high 32 bits
	stw	r.0, 4(Product)

	LEAF_EXIT(HalpMultiply)