////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Nestopia is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
////////////////////////////////////////////////////////////////////////////////////////

#include "NstCore.hpp"

#if NST_MSVC

 #if (defined(_DEBUG) && defined(NDEBUG)) || (!defined(_DEBUG) && !defined(NDEBUG))
 #pragma message("warning, NDEBUG and _DEBUG macro inconsistency!")
 #endif

 #if !defined(_DEBUG) && defined(__MSVC_RUNTIME_CHECKS)
 #pragma message("performance warning, RTCx compiler options enabled in non-debug mode!")
 #endif

 #ifdef _CPPRTTI
 #pragma message("performance warning, RTTI compiler option needlessly enabled!")
 #endif

#endif

#ifndef NST_NATIVE_QWORD

#if NST_MSVC || NST_MWERKS >= 0x3000 || NST_BCB >= 0x600
#pragma message("performance warning, no native 64bit integer support!")
#elif NST_GCC && !defined(__STRICT_ANSI__)
#warning "performance warning, no native 64bit integer support!"
#endif

#include "NstAssert.hpp"

namespace Nes
{
	void qaword::Multiply(qaword multiplier)
	{
		qaword multiplicand(*this);
		lo = 0;
		hi = 0;

		while (multiplicand)
		{
			if (multiplicand.lo & 0x1)
				(*this) += multiplier;

			multiplicand.lo = (multiplicand.lo >> 1) | (multiplicand.hi << 31 & LO_MASK);
			multiplicand.hi >>= 1;

			multiplier.hi = (multiplier.hi << 1 & LO_MASK) | (multiplier.lo >> 31);
			multiplier.lo = (multiplier.lo << 1 & LO_MASK);
		}
	}

	void qaword::Divide(qaword& dividend,const qaword divisor,const bool mod)
	{
		NST_ASSERT( bool(divisor) );

		qaword remainder(0);
		qaword quotient(0);

		if (divisor < dividend)
		{
			uint bits = 64;

			do
			{
				remainder.hi = (remainder.hi << 1 & LO_MASK) | (remainder.lo >> 31);
				remainder.lo = (remainder.lo << 1 & LO_MASK) | (dividend.hi >> 31);
				dividend.hi = (dividend.hi << 1 & LO_MASK) | (dividend.lo >> 31);
				dividend.lo = (dividend.lo << 1 & LO_MASK);
				--bits;
			}
			while (remainder < divisor);

			for (;;)
			{
				qaword tmp(remainder);
				tmp -= divisor;

				quotient.hi = (quotient.hi << 1 & LO_MASK) | (quotient.lo >> 31);
				quotient.lo = (quotient.lo << 1 & LO_MASK);

				if (!(tmp.hi & LO_MSB))
				{
					quotient.lo |= 0x1;
					remainder = tmp;
				}

				if (!bits)
					break;

				--bits;

				remainder.hi = (remainder.hi << 1 & LO_MASK) | (remainder.lo >> 31);
				remainder.lo = (remainder.lo << 1 & LO_MASK) | (dividend.hi >> 31);
				dividend.hi = (dividend.hi << 1 & LO_MASK) | (dividend.lo >> 31);
				dividend.lo = (dividend.lo << 1 & LO_MASK);
			}
		}
		else if (divisor == dividend)
		{
			quotient = 1;
		}
		else
		{
			remainder = dividend;
		}

		if (!mod)
			dividend = quotient;
		else
			dividend = remainder;
	}

	void qaword::Shl(const uint v)
	{
		NST_ASSERT( v < 64 );

		if (v)
		{
			if (v < 32)
			{
				dword t = lo >> (32-v);
				lo = (lo << v) & LO_MASK;
				hi = (hi << v | t) & LO_MASK;
			}
			else
			{
				hi = (lo << (v-32)) & LO_MASK;
				lo = 0;
			}
		}
	}

	void qaword::Shr(const uint v)
	{
		NST_ASSERT( v < 64 );

		if (v)
		{
			if (v < 32)
			{
				dword t = (hi << (32-v)) & LO_MASK;
				hi = hi >> v;
				lo = lo >> v | t;
			}
			else
			{
				lo = hi >> (v-32);
				hi = 0;
			}
		}
	}
}

#endif
