//
// Project UnReality: The First Nintendo 64 Emulator
// Copyright (C) 1997-1998, Cold Fusion Entertainment
// All rights reserved
//
// This source code is unpublished propietary information of Cold Fusion Entertainment.  No portion of this software
// may be disclosed to any third party or copied or duplicated in any form, in whole or in part, without the prior
// written consent of Cold Fusion Entertainment.
// 

//
// $Log: INLINE.CPP $
// Revision 1.7  1998/04/23 23:35:23  BREAKPOINT
// Added code to RSP reads and writes to support misaligned memory accesses.
// Revision 1.6  1998/04/07 23:29:39  BREAKPOINT
// Fixed up debugger... added 'Safe_Read*()' calls to access memory without causing crashes when reading from unmapped memory.
// Revision 1.5  1998/04/05 15:09:33  BREAKPOINT
// Added extra variables to MEM_* functions so RSP can call as well...
// Revision 1.4  1998/04/02 21:18:19  BREAKPOINT
// Implemented new memory mapping routines and changed ROM loading code.
// Revision 1.3  1998/01/31 19:51:28  BREAKPOINT
// Major changes.  Removed most calls and rewrote them in assembly.
// Revision 1.2  1998/01/10 21:57:32  BREAKPOINT
// Initial changes to move to assembly version of R4300i assembly core
// Revision 1.1  1998/01/10 01:27:42  BREAKPOINT
// Initial revision
//

//---------------------------------------------------------------------------------------------------------------------

#include <types.h>
#include "debugger.h"
#include "mmio.h"

// Inline functions...
inline uchar  Read8(ulong addr);
inline ushort Read16(ulong addr);
inline ulong  Read32(ulong addr);
inline ulong *Read64(ulong addr);
inline void   Write8(ulong addr, uchar val);
inline void   Write16(ulong addr, ushort val);
inline void   Write32(ulong addr, ulong val);
inline void   Write64(ulong addr, ulong val[2]);

inline uchar  RSP_Read8(ulong addr);
inline ushort RSP_Read16(ulong addr);
inline ulong  RSP_Read32(ulong addr);
inline ulong *RSP_Read64(ulong addr);
inline void   RSP_Write8(ulong addr, uchar val);
inline void   RSP_Write16(ulong addr, ushort val);
inline void   RSP_Write32(ulong addr, ulong val);
inline void   RSP_Write64(ulong addr, ulong val[2]);

// "Safe" memory reads that do not raise an exception under any circumstances.  Sets 'is_outside_memory' if the
// address is not a valid one.
inline uchar  Debug_Read8(ulong addr);
inline ushort Debug_Read16(ulong addr);
inline ulong  Debug_Read32(ulong addr);

// Used for generating the 'raw_opcode' stuff.  Appropriately sets 'is_outside_memory' if so.
inline void   MakeDebugOpcode(void);


inline void sprintf64(char *buf, ulong val[2])
{
    sprintf(buf, "%08X%08X", val[0], val[1]);
}

inline ulong read_ulong(ulong offs)
{
    ulong t;
    uchar *p = (uchar *)&t;

    *(p + 3) = ((uchar *)CART1DOM)[offs    ];
    *(p + 2) = ((uchar *)CART1DOM)[offs + 1];
    *(p + 1) = ((uchar *)CART1DOM)[offs + 2];
    *(p    ) = ((uchar *)CART1DOM)[offs + 3];

    return t;
}

// Read/write functions which are 'trusted'.  Called by MMIO emulation code, which knows where to read/write.
inline uchar Read8(ulong addr)
{
	MEM_addr = addr;
	MEM_Read8();
	return (uchar)MEM_return_val[0];
}

inline ushort Read16(ulong addr)
{
	MEM_addr = addr;
	MEM_Read16();
	return (ushort)MEM_return_val[0];
}

inline ulong Read32(ulong addr)
{
	MEM_addr = addr;
	MEM_Read32();
	return MEM_return_val[0];
}

inline ulong *Read64(ulong addr)
{
	MEM_addr = addr;
	MEM_Read64();
	return &MEM_return_val[0];
}

inline void Write8(ulong addr, uchar val)
{
	MEM_addr   = addr;
	MEM_val[0] = val;
	MEM_Write8();
}

inline void Write16(ulong addr, ushort val)
{
	MEM_addr   = addr;
	MEM_val[0] = val;
	MEM_Write16();
}

inline void Write32(ulong addr, ulong val)
{
	MEM_addr   = addr;
	MEM_val[0] = val;
	MEM_Write32();
}

inline void Write64(ulong addr, ulong val[2])
{
	MEM_addr = addr;
	MEM_val[0] = val[0];
	MEM_val[1] = val[1];
	MEM_Write64();
}

// General read/write functions to/from memory.  Called by emulation code.
inline uchar ReadMemoryByte(ulong addr)
{
	MEM_addr = addr;
	MMIO_Read8();
	return (uchar)MEM_return_val[0];
}

inline ushort ReadMemoryWord(ulong addr)
{
	MEM_addr = addr;
	MMIO_Read16();
	return (ushort)MEM_return_val[0];
}

inline ulong ReadMemoryDword(ulong addr)
{
	MEM_addr = addr;
	MMIO_Read32();
	return MEM_return_val[0];
}

inline ulong *ReadMemory64(ulong addr)
{
	MEM_addr = addr;
	MMIO_Read64();
	return &MEM_return_val[0];
}

inline void WriteMemoryByte(ulong addr, uchar val)
{
	MEM_addr = addr;
	MEM_val[0] = val;
	MMIO_Write8();
}

inline void WriteMemoryWord(ulong addr, ushort val)
{
	MEM_addr = addr;
	MEM_val[0] = val;
	MMIO_Write16();
}

inline void WriteMemoryDword(ulong addr, ulong val)
{
	MEM_addr = addr;
	MEM_val[0] = val;
	MMIO_Write32();
}

inline void WriteMemory64(ulong addr, ulong val[2])
{
	MEM_addr = addr;
	MEM_val[0] = val[0];
	MEM_val[1] = val[1];
	MMIO_Write64();
}

inline ulong SignExtend8(uchar a)
{
    ulong b;

    __asm {
        push eax
        movsx eax, a
        mov b, eax
        pop eax
    };

    return b;
}

inline ulong SignExtend16(ushort a)
{
    ulong b;

	__asm {
        push eax
        movsx eax, a
        mov b, eax
        pop eax
    };

	return b;
}

// Read/write functions which are called by the RSP.  These do NOT invoke an MMIO callback.  These functions also
// handle unaligned memory properly.

inline uchar RSP_Read8(ulong addr)
{
	RSP_MEM_addr = addr;
	RSP_MEM_Read8();
	return (uchar)RSP_MEM_return_val[0];
}

inline ushort RSP_Read16(ulong addr)
{
	if (addr & 0x1)
	{
		return ((ushort)RSP_Read8(addr) << 8) | RSP_Read8(addr + 1);
	}
	else
	{
		RSP_MEM_addr = addr;
		RSP_MEM_Read16();
		return (ushort)RSP_MEM_return_val[0];
	}
}

inline ulong RSP_Read32(ulong addr)
{
	if (addr & 0x3)
	{
		return ((ulong)RSP_Read8(addr    ) << 24) | ((ulong)RSP_Read8(addr + 1) << 16) |
			   ((ulong)RSP_Read8(addr + 2) << 8 ) | RSP_Read8(addr + 3);
	}
	else
	{
		RSP_MEM_addr = addr;
		RSP_MEM_Read32();
		return RSP_MEM_return_val[0];
	}
}

inline ulong *RSP_Read64(ulong addr)
{
	if (addr & 0x7)
	{
		RSP_MEM_return_val[0] = ((ulong)RSP_Read8(addr    ) << 24) | ((ulong)RSP_Read8(addr + 1) << 16) |
			                    ((ulong)RSP_Read8(addr + 2) << 8 ) | RSP_Read8(addr + 3);

		RSP_MEM_return_val[1] = ((ulong)RSP_Read8(addr + 4) << 24) | ((ulong)RSP_Read8(addr + 5) << 16) |
			                    ((ulong)RSP_Read8(addr + 6) << 8 ) | RSP_Read8(addr + 7);
	}
	else
	{
		RSP_MEM_addr = addr;
		RSP_MEM_Read64();
	}

	return &RSP_MEM_return_val[0];
}

inline void RSP_Write8(ulong addr, uchar val)
{
	RSP_MEM_addr   = addr;
	RSP_MEM_val[0] = val;
	RSP_MEM_Write8();
}

inline void RSP_Write16(ulong addr, ushort val)
{
	if (addr & 0x1)
	{
		RSP_MEM_addr   = addr;
		RSP_MEM_val[0] = (uchar)(val >> 8);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 1;
		RSP_MEM_val[0] = (uchar)val;
		RSP_MEM_Write8();
	}
	else
	{
		RSP_MEM_addr   = addr;
		RSP_MEM_val[0] = val;
		RSP_MEM_Write16();
	}
}

inline void RSP_Write32(ulong addr, ulong val)
{
	if (addr & 0x3)
	{
		RSP_MEM_addr   = addr;
		RSP_MEM_val[0] = (uchar)(val >> 24);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 1;
		RSP_MEM_val[0] = (uchar)(val >> 16);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 2;
		RSP_MEM_val[0] = (uchar)(val >> 8);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 3;
		RSP_MEM_val[0] = (uchar)val;
		RSP_MEM_Write8();
	}
	else
	{
		RSP_MEM_addr   = addr;
		RSP_MEM_val[0] = val;
		RSP_MEM_Write32();
	}
}

inline void RSP_Write64(ulong addr, ulong val[2])
{
	if (addr & 0x7)
	{
		RSP_MEM_addr   = addr;
		RSP_MEM_val[0] = (uchar)(val[0] >> 24);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 1;
		RSP_MEM_val[0] = (uchar)(val[0] >> 16);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 2;
		RSP_MEM_val[0] = (uchar)(val[0] >> 8);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 3;
		RSP_MEM_val[0] = (uchar)val[0];
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 4;
		RSP_MEM_val[0] = (uchar)(val[1] >> 24);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 5;
		RSP_MEM_val[0] = (uchar)(val[1] >> 16);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 6;
		RSP_MEM_val[0] = (uchar)(val[1] >> 8);
		RSP_MEM_Write8();

		RSP_MEM_addr   = addr + 7;
		RSP_MEM_val[0] = (uchar)val[1];
		RSP_MEM_Write8();
	}
	else
	{
		RSP_MEM_addr = addr;
		RSP_MEM_val[0] = val[0];
		RSP_MEM_val[1] = val[1];
		RSP_MEM_Write64();
	}
}

inline uchar Debug_Read8(ulong addr)
{
	is_outside_memory = FALSE;

	__try
	{
		return Read8(addr);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		is_outside_memory = TRUE;
	}
}

inline ushort Debug_Read16(ulong addr)
{
	is_outside_memory = FALSE;

	__try
	{
		return Read16(addr);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		is_outside_memory = TRUE;
	}
}

inline ulong Debug_Read32(ulong addr)
{
	is_outside_memory = FALSE;

	__try
	{
		return Read32(addr);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		is_outside_memory = TRUE;
	}
}

inline void MakeDebugOpcode(void)
{
	ulong temp;

	raw_opcode_ulong = Debug_Read32(raw_opcode_addr);
	temp = raw_opcode_ulong;

	raw_opcode[3] = (uchar)temp; temp >>= 8;
	raw_opcode[2] = (uchar)temp; temp >>= 8;
	raw_opcode[1] = (uchar)temp; temp >>= 8;
	raw_opcode[0] = (uchar)temp;
}
