/*
* The contents of this file are subject to the JJOS Public License
* Version 0.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.jos.org/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is JJOS code, released August, 1999.
*
* The Initial Developer of the Original Code is Hilary Cheng.
* Portions created by Hilary Cheng are Copyright (C) 1999.
* All rights reserved.
*
* Contributor(s):
*
*/

#include "java_object.h"
#include "long64.h"

// value = value << 1
void _lshl(jlong *value, int shift) {
  int sign = value->myHigh32 & 0x80000000;
  int lastbit = 0;
  if (shift <= 0) return;
  for (int c=0;c < shift;c++) {
    lastbit = value->myLow32 & 0x80000000;
    value->myHigh32 = value->myHigh32 << 1;
    value->myLow32 = value->myLow32 << 1;
    value->myHigh32 |= sign;
    if (lastbit) value->myHigh32 |= 1;
  }
}

// value = value >> shift
void _lshr(jlong *value, int shift) {
  int sign = value->myHigh32 & 0x80000000;
  int lastbit = 0;
  if (shift <= 0) return;
  for (int c=0;c < shift;c++) {
    lastbit = value->myHigh32 & 1;
    value->myHigh32 = value->myHigh32 >> 1;
    value->myLow32 = value->myLow32 >> 1;
    value->myHigh32 |= sign;
    if (lastbit) value->myLow32 |= 0x80000000;
  }
}

// c = a + b
void _ladd(jlong *c, jlong *a, jlong *b) {
  int carry = 0;
  jlong temp;
  temp.myLow32 = c->myLow32; temp.myHigh32 = c->myHigh32;
  temp.myLow32 = a->myLow32 + b->myLow32;
  carry = (a->myLow32 > temp.myLow32) | (b->myLow32 > temp.myLow32);
  carry = carry ? 1 : 0;
  temp.myHigh32 = a->myHigh32 + b->myHigh32 + carry;
  c->myHigh32 = temp.myHigh32; c->myLow32 = temp.myLow32;
}

// value = -value;
void _lneg(jlong *value) {
  jlong constant_one, copy;

  constant_one.myHigh32 = 0; constant_one.myLow32 = 1;
  value->myLow32 = ~value->myLow32;
  copy.myLow32 = value->myLow32;
  value->myHigh32 = ~value->myHigh32;
  copy.myHigh32 = value->myHigh32;
  _ladd(value, &copy, &constant_one);
}

// c = a - b
void _lsub(jlong *c, jlong *a, jlong *b) {
  jlong temp;
  temp.myHigh32 = b->myHigh32;
  temp.myLow32 = b->myLow32;
  _lneg(&temp);
  _ladd(c, a, &temp);
}

// c = a & b
void _land(jlong *c, jlong *a, jlong *b) {
  c->myLow32 = a->myLow32 & b->myLow32;
  c->myHigh32 = a->myHigh32 & b->myHigh32;
}

// c = a ^ b
void _lxor(jlong *c, jlong *a, jlong *b) {
  c->myLow32 = a->myLow32 ^ b->myLow32;
  c->myHigh32 = a->myHigh32 ^ b->myHigh32;
}

// c = a | b
void _lor(jlong *c, jlong *a, jlong *b) {
  c->myLow32 = a->myLow32 | b->myLow32;
  c->myHigh32 = a->myHigh32 | b->myHigh32;
}

// result = jl2 / jl1;
void _ldiv(jlong *jl1, jlong *jl2, jlong *result)
{
  jlong jl1temp, jl2temp, jl3, scan, temp;
  int counter = 0;

  jl1temp.myHigh32 = jl1->myHigh32;
  jl1temp.myLow32 = jl1->myLow32;
  jl2temp.myHigh32 = jl2->myHigh32;
  jl2temp.myLow32 = jl2->myLow32;
  scan.myHigh32 = 0x40000000;
  scan.myLow32 = 0;
  result->myHigh32 = 0; result->myLow32 = 0;

  if (jl1->myHigh32 & 0x80000000) _lneg(&jl1temp);
  if (jl2->myHigh32 & 0x80000000) _lneg(&jl2temp);

  while (temp.myHigh32 == 0 && temp.myLow32 == 0) {
    _land(&temp, &jl1temp, &scan);
    if (temp.myHigh32 == 0 && temp.myLow32 == 0) {
      _lshr(&scan, 1); counter++;
    }
  }

  _lshl(&jl1temp, counter);

  for (int loop=0;loop < counter + 1;loop++) {
    _lshl(result, 1);
    _lsub(&temp, &jl2temp, &jl1temp);
    if (!(temp.myHigh32 & 0x80000000)) {
      jl2temp.myHigh32 = temp.myHigh32;
      jl2temp.myLow32 = temp.myLow32;
      result->myLow32 |= 1;
    }
    _lshr(&jl1temp, 1);
  }

  if ((jl1->myHigh32 & 0x80000000) ^ (jl2->myHigh32 & 0x80000000)) {
    _lneg(result);
  }
}

// lresult = jl2 % jl1;
void _lrem(jlong *jl1, jlong *jl2, jlong *lresult)
{
  jlong jl1temp, jl2temp, jl3, scan, temp;
  jlong result, lresult;
  int counter = 0;

  jl1temp.myHigh32 = jl1->myHigh32;
  jl1temp.myLow32 = jl1->myLow32;
  jl2temp.myHigh32 = jl2->myHigh32;
  jl2temp.myLow32 = jl2->myLow32;
  scan.myHigh32 = 0x40000000;
  scan.myLow32 = 0;
  result.myHigh32 = 0; result.myLow32 = 0;
  lresult->myHigh32 = 0; lresult->myLow32 = 0;

  if (jl1.myHigh32 & 0x80000000) _lneg(&jl1temp);
  if (jl2.myHigh32 & 0x80000000) _lneg(&jl2temp);

  while (temp.myHigh32 == 0 && temp.myLow32 == 0) {
    _land(&temp, &jl1temp, &scan);
    if (temp.myHigh32 == 0 && temp.myLow32 == 0) {
      _lshr(&scan, 1); counter++;
    }
  }

  _lshl(&jl1temp, counter);

  for (int loop=0;loop < counter + 1;loop++) {
    _lsub(&temp, &jl2temp, &jl1temp);
    _lshl(&result, 1);
    if (!(temp.myHigh32 & 0x80000000)) {
      jl2temp.myHigh32 = temp.myHigh32;
      jl2temp.myLow32 = temp.myLow32;
      lresult->myHigh32 = temp.myHigh32;
      lresult->myLow32 = temp.myLow32;
    }
    _lshr(&jl1temp, 1);
  }

  if (jl2.myHigh32 & 0x80000000) _lneg(lresult);
      
  if ((jl1.myHigh32 & 0x80000000) ^ (jl2.myHigh32 & 0x80000000)) {
    _lneg(result);
  }
}

// result = jl1 * jl2
void _lmul(jlong *jl1, jlong *jl2, jlong *result)
{
  jlong jl1temp, jl2temp, jl3, scan, temp;
  int counter = 0;

  jl1temp.myHigh32 = jl1->myHigh32;
  jl1temp.myLow32 = jl1->myLow32;
  jl2temp.myHigh32 = jl2->myHigh32;
  jl2temp.myLow32 = jl2->myLow32;
  scan.myHigh32 = 0; scan.myLow32 = 1;
  result.myHigh32 = 0; result.myLow32 = 0;

  if (jl1.myHigh32 & 0x80000000) _lneg(&jl1temp);
  if (jl2.myHigh32 & 0x80000000) _lneg(&jl2temp);

  counter = 0;
  while (scan.myHigh32 != 0x80000000 && counter < 63) {
    _land(&temp, &jl2temp, &scan);
    if (!(temp.myHigh32 == 0 && temp.myLow32 == 0)) {
	_ladd(result, result, &jl1temp);
    }
    counter++; _lshl(&scan, 1);
    _lshl(&jl1temp, 1);
  }

  if ((jl1.myHigh32 & 0x80000000) ^ (jl2.myHigh32 & 0x80000000)) {
    _lneg(result);
  }
}
