/*
 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
 *
 * (c) Copyright 1996, 1997, 1998 Gary Henderson (gary@daniver.demon.co.uk) and
 *                                Jerremy Koot (jkoot@euronet.nl)
 *
 * Super FX C emulator code 
 * (c) Copyright 1997, 1998 Lestat (lstat@hotmail.com) and
 *                          Gary Henderson.
 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
 *
 * DSP1 emulator code (c) Copyright 1998 Lestat and Gary Henderson.
 * DOS port code contains the works of other authors. See headers in
 * individual files.
 *
 * Permission to use, copy, modify and distribute Snes9x in both binary and
 * source form, for non-commercial purposes, is hereby granted without fee,
 * providing that this license information and copyright notice appear with
 * all copies and any derived work.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event shall the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Snes9x is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Snes9x or software derived from Snes9x.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so everyone can benefit from the modifications
 * in future versions.
 *
 * Super NES and Super Nintendo Entertainment System are trademarks of
 * Nintendo Co., Limited and its subsidiary companies.
 */

#ifndef _GETSET_H_
#define _GETSET_H_

#include "ppu.h"
#include "dsp1.h"

INLINE uint8 S9xGetByte (uint32 Address)
{
#if defined(VAR_CYCLES) || defined(CPU_SHUTDOWN)
    int block;
    uint8 *GetAddress = Memory.Map [block = (Address >> 13) & 0x7ff];
#else
    uint8 *GetAddress = Memory.Map [(Address >> 13) & 0x7ff];
#endif    
    if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
    {
#ifdef VAR_CYCLES
	CPU.Cycles += Memory.MemorySpeed [block];
#endif
#ifdef CPU_SHUTDOWN
	if (Memory.BlockIsRAM [block])
	    CPU.WaitAddress = CPU.PCAtOpcodeStart;
#endif
	return (*(GetAddress + (Address & 0xffff)));
    }

    switch ((int) GetAddress)
    {
    case CMemory::MAP_PPU:
#ifdef VAR_CYCLES
	if (!CPU.InDMA)
	    CPU.Cycles += ONE_CYCLE;
#endif	
	return (S9xGetPPU (Address & 0xffff));
    case CMemory::MAP_CPU:
#ifdef VAR_CYCLES   
	CPU.Cycles += ONE_CYCLE;
#endif
	return (S9xGetCPU (Address & 0xffff));
    case CMemory::MAP_DSP:
#ifdef VAR_CYCLES
	CPU.Cycles += 8;
#endif	
	return (S9xGetDSP (Address & 0xffff));
    case CMemory::MAP_LOROM_SRAM:
#ifdef VAR_CYCLES
	CPU.Cycles += 8;
#endif
	return (*(Memory.SRAM + ((Address & Memory.SRAMMask))));

    case CMemory::MAP_HIROM_SRAM:
#ifdef VAR_CYCLES
	CPU.Cycles += 8;
#endif
	return (*(Memory.SRAM + (((Address & 0x7fff) - 0x6000 +
				  ((Address & 0xf0000) >> 3)) & Memory.SRAMMask)));

    case CMemory::MAP_DEBUG:
#ifdef DEBUGGER
	printf ("R(B) %06x\n", Address);
#endif

    default:
    case CMemory::MAP_NONE:
#ifdef VAR_CYCLES
	CPU.Cycles += 8;
#endif
	return (0);
    }
}

INLINE uint16 S9xGetWord (uint32 Address)
{
#if defined(VAR_CYCLES) || defined(CPU_SHUTDOWN)
    int block;
    uint8 *GetAddress = Memory.Map [block = (Address >> 13) & 0x7ff];
#else
    uint8 *GetAddress = Memory.Map [(Address >> 13) & 0x7ff];
#endif    
    if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
    {
#ifdef VAR_CYCLES
	CPU.Cycles += Memory.MemorySpeed [block] << 1;
#endif
#ifdef CPU_SHUTDOWN
	if (Memory.BlockIsRAM [block])
	    CPU.WaitAddress = CPU.PCAtOpcodeStart;
#endif
#ifdef FAST_LSB_WORD_ACCESS
	return (*(uint16 *) (GetAddress + (Address & 0xffff)));
#else
	return (*(GetAddress + (Address & 0xffff)) |
		(*(GetAddress + (Address & 0xffff) + 1) << 8));
#endif	
    }

    switch ((int) GetAddress)
    {
    case CMemory::MAP_PPU:
#ifdef VAR_CYCLES
	if (!CPU.InDMA)
	    CPU.Cycles += TWO_CYCLES;
#endif	
	return (S9xGetPPU (Address & 0xffff) |
		(S9xGetPPU ((Address + 1) & 0xffff) << 8));
    case CMemory::MAP_CPU:
#ifdef VAR_CYCLES   
	CPU.Cycles += TWO_CYCLES;
#endif
	return (S9xGetCPU (Address & 0xffff) |
		(S9xGetCPU ((Address + 1) & 0xffff) << 8));
    case CMemory::MAP_DSP:
#ifdef VAR_CYCLES
	CPU.Cycles += 16;
#endif	
	return (S9xGetDSP (Address & 0xffff) |
		(S9xGetDSP ((Address + 1) & 0xffff) << 8));
    case CMemory::MAP_LOROM_SRAM:
#ifdef VAR_CYCLES
	CPU.Cycles += 16;
#endif
#ifdef FAST_LSB_WORD_ACCESS
	return (*(uint16 *) (Memory.SRAM + (Address & Memory.SRAMMask)));
#else
	GetAddress = Memory.SRAM + (Address & Memory.SRAMMask);
	return (*GetAddress | (*(GetAddress + 1) << 8));
#endif	

    case CMemory::MAP_HIROM_SRAM:
#ifdef VAR_CYCLES
	CPU.Cycles += 16;
#endif
#ifdef FAST_LSB_WORD_ACCESS
	return (*(uint16 *) (Memory.SRAM +
		  (((Address & 0x7fff) - 0x6000 +
		    ((Address & 0xf0000) >> 3)) & Memory.SRAMMask)));
#else
	
	GetAddress = (Memory.SRAM +
		      (((Address & 0x7fff) - 0x6000 +
			((Address & 0xf0000) >> 3) & Memory.SRAMMask)));
	return (*GetAddress + (*(GetAddress + 1) << 8));
#endif

    case CMemory::MAP_DEBUG:
#ifdef DEBUGGER
	printf ("R(W) %06x\n", Address);
#endif

    default:
    case CMemory::MAP_NONE:
#ifdef VAR_CYCLES
	CPU.Cycles += 16;
#endif
	return (0);
    }
}

INLINE void S9xSetByte (uint8 Byte, uint32 Address)
{
#ifdef CPU_SHUTDOWN
    CPU.WaitAddress = NULL;
#endif
    uint8 *SetAddress = Memory.WriteMap [(Address >> 13) & 0x7ff];

    if (SetAddress >= (uint8 *) CMemory::MAP_LAST)
    {
#ifdef VAR_CYCLES
	CPU.Cycles += 8;
#endif
	*(SetAddress + (Address & 0xffff)) = Byte;
	return;
    }

    switch ((int) SetAddress)
    {
    case CMemory::MAP_PPU:
#ifdef VAR_CYCLES
	if (!CPU.InDMA)
	    CPU.Cycles += ONE_CYCLE;
#endif	
	S9xSetPPU (Byte, Address & 0xffff);
	return;

    case CMemory::MAP_CPU:
#ifdef VAR_CYCLES   
	CPU.Cycles += ONE_CYCLE;
#endif
	S9xSetCPU (Byte, Address & 0xffff);
	return;

    case CMemory::MAP_DSP:
#ifdef VAR_CYCLES
	CPU.Cycles += 8;
#endif	
	S9xSetDSP (Byte, Address & 0xffff);
	return;

    case CMemory::MAP_LOROM_SRAM:
#ifdef VAR_CYCLES
	CPU.Cycles += 8;
#endif
	if (Memory.SRAMMask)
	    *(Memory.SRAM + (Address & Memory.SRAMMask)) = Byte;
	return;

    case CMemory::MAP_HIROM_SRAM:
#ifdef VAR_CYCLES
	CPU.Cycles += 8;
#endif
	if (Memory.SRAMMask)
	    *(Memory.SRAM + (((Address & 0x7fff) - 0x6000 +
			      ((Address & 0xf0000) >> 3)) & Memory.SRAMMask)) = Byte;
	return;

    case CMemory::MAP_DEBUG:
#ifdef DEBUGGER
	printf ("W(B) %06x\n", Address);
#endif

    default:
    case CMemory::MAP_NONE:
#ifdef VAR_CYCLES    
	CPU.Cycles += 8;
#endif	
	return;
    }
}

INLINE void S9xSetWord (uint16 Word, uint32 Address)
{
#ifdef CPU_SHUTDOWN
    CPU.WaitAddress = NULL;
#endif
    uint8 *SetAddress = Memory.WriteMap [(Address >> 13) & 0x7ff];

    if (SetAddress >= (uint8 *) CMemory::MAP_LAST)
    {
#ifdef VAR_CYCLES
	CPU.Cycles += 16;
#endif
#ifdef FAST_LSB_WORD_ACCESS
	*(uint16 *) (SetAddress + (Address & 0xffff)) = Word;
#else
	*(SetAddress + (Address & 0xffff)) = (uint8) Word;
	*(SetAddress + (Address & 0xffff) + 1) = Word >> 8;
#endif
	return;
    }

    switch ((int) SetAddress)
    {
    case CMemory::MAP_PPU:
#ifdef VAR_CYCLES
	if (!CPU.InDMA)
	    CPU.Cycles += TWO_CYCLES;
#endif	
	S9xSetPPU ((uint8) Word, Address & 0xffff);
	S9xSetPPU (Word >> 8, (Address & 0xffff) + 1);
	return;

    case CMemory::MAP_CPU:
#ifdef VAR_CYCLES   
	CPU.Cycles += TWO_CYCLES;
#endif
	S9xSetCPU ((uint8) Word, (Address & 0xffff));
	S9xSetCPU (Word >> 8, (Address & 0xffff) + 1);
	return;

    case CMemory::MAP_DSP:
#ifdef VAR_CYCLES
	CPU.Cycles += 16;
#endif	
	S9xSetDSP ((uint8) Word, (Address & 0xffff));
	S9xSetDSP (Word >> 8, (Address & 0xffff) + 1);
	return;

    case CMemory::MAP_LOROM_SRAM:
#ifdef VAR_CYCLES
	CPU.Cycles += 16;
#endif
	if (Memory.SRAMMask)
	{
#ifdef FAST_LSB_WORD_ACCESS
	    *(uint16 *) (Memory.SRAM + (Address & Memory.SRAMMask)) = Word;
#else	    
	    SetAddress = Memory.SRAM + (Address & Memory.SRAMMask);
	    *SetAddress = (uint8) Word;
	    *(SetAddress + 1) = Word >> 8;
#endif	    
	}
	return;

    case CMemory::MAP_HIROM_SRAM:
#ifdef VAR_CYCLES
	CPU.Cycles += 16;
#endif
	if (Memory.SRAMMask)
	{
#ifdef FAST_LSB_WORD_ACCESS
	    *(uint16 *) (Memory.SRAM +
	      (((Address & 0x7fff) - 0x6000 +
		((Address & 0xf0000) >> 3)) & Memory.SRAMMask)) = Word;
#else	      
	    SetAddress = Memory.SRAM +
			 (((Address & 0x7fff) - 0x6000 +
			   ((Address & 0xf0000) >> 13) & Memory.SRAMMask));
	    *SetAddress = (uint8) Word;
	    *(SetAddress + 1) = Word >> 8;
#endif	      
	}
	return;

    case CMemory::MAP_DEBUG:
#ifdef DEBUGGER
	printf ("W(W) %06x\n", Address);
#endif

    default:
    case CMemory::MAP_NONE:
#ifdef VAR_CYCLES    
	CPU.Cycles += 16;
#endif
	return;
    }
}

INLINE uint8 *GetBasePointer (uint32 Address)
{
    uint8 *GetAddress = Memory.Map [(Address >> 13) & 0x7ff];
    if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
	return (GetAddress);

    switch ((int) GetAddress)
    {
    case CMemory::MAP_PPU:
	return (Memory.FillRAM - 0x2000);
    case CMemory::MAP_CPU:
	return (Memory.FillRAM - 0x4000);
    case CMemory::MAP_DSP:
	return (Memory.FillRAM - 0x6000);
    case CMemory::MAP_LOROM_SRAM:
	return (Memory.SRAM);
    case CMemory::MAP_HIROM_SRAM:
	return (Memory.SRAM - 0x6000);
    case CMemory::MAP_DEBUG:
#ifdef DEBUGGER
	printf ("GBP %06x\n", Address);
#endif

    default:
    case CMemory::MAP_NONE:
	return (0);
    }
}

INLINE uint8 *S9xGetMemPointer (uint32 Address)
{
    uint8 *GetAddress = Memory.Map [(Address >> 13) & 0x7ff];
    if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
	return (GetAddress + (Address & 0xffff));

    switch ((int) GetAddress)
    {
    case CMemory::MAP_PPU:
	return (Memory.FillRAM - 0x2000 + (Address & 0xffff));
    case CMemory::MAP_CPU:
	return (Memory.FillRAM - 0x4000 + (Address & 0xffff));
    case CMemory::MAP_DSP:
	return (Memory.FillRAM - 0x6000 + (Address & 0xffff));
    case CMemory::MAP_LOROM_SRAM:
	return (Memory.SRAM + (Address & 0xffff));
    case CMemory::MAP_HIROM_SRAM:
	return (Memory.SRAM - 0x6000 + (Address & 0xffff));

    case CMemory::MAP_DEBUG:
#ifdef DEBUGGER
	printf ("GMP %06x\n", Address);
#endif
    default:
    case CMemory::MAP_NONE:
	return (0);
    }
}

INLINE void S9xSetPCBase (uint32 Address)
{
#ifdef VAR_CYCLES
    int block;
    uint8 *GetAddress = Memory.Map [block = (Address >> 13) & 0x7ff];
#else
    uint8 *GetAddress = Memory.Map [(Address >> 13) & 0x7ff];
#endif    
    if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
    {
#ifdef VAR_CYCLES
	CPU.MemSpeed = Memory.MemorySpeed [block];
	CPU.MemSpeedx2 = CPU.MemSpeed << 1;
#endif
	CPU.PCBase = GetAddress;
	CPU.PC = GetAddress + (Address & 0xffff);
	return;
    }

    switch ((int) GetAddress)
    {
    case CMemory::MAP_PPU:
#ifdef VAR_CYCLES
	CPU.MemSpeed = ONE_CYCLE;
	CPU.MemSpeedx2 = TWO_CYCLES;
#endif	
	CPU.PCBase = Memory.FillRAM - 0x2000;
	CPU.PC = CPU.PCBase + (Address & 0xffff);
	return;
	
    case CMemory::MAP_CPU:
#ifdef VAR_CYCLES   
	CPU.MemSpeed = ONE_CYCLE;
	CPU.MemSpeedx2 = TWO_CYCLES;
#endif
	CPU.PCBase = Memory.FillRAM - 0x4000;
	CPU.PC = CPU.PCBase + (Address & 0xffff);
	return;
	
    case CMemory::MAP_DSP:
#ifdef VAR_CYCLES
	CPU.MemSpeed = 8;
	CPU.MemSpeedx2 = 16;
#endif	
	CPU.PCBase = Memory.FillRAM - 0x6000;
	CPU.PC = CPU.PCBase + (Address & 0xffff);
	return;
	
    case CMemory::MAP_LOROM_SRAM:
#ifdef VAR_CYCLES
	CPU.MemSpeed = 8;
	CPU.MemSpeedx2 = 16;
#endif
	CPU.PCBase = Memory.SRAM;
	CPU.PC = CPU.PCBase + (Address & 0xffff);
	return;

    case CMemory::MAP_HIROM_SRAM:
#ifdef VAR_CYCLES
	CPU.MemSpeed = 8;
	CPU.MemSpeedx2 = 16;
#endif
	CPU.PCBase = Memory.SRAM - 0x6000;
	CPU.PC = CPU.PCBase + (Address & 0xffff);
	return;

    case CMemory::MAP_DEBUG:
#ifdef DEBUGGER
	printf ("SBP %06x\n", Address);
#endif
	
    default:
    case CMemory::MAP_NONE:
#ifdef VAR_CYCLES
	CPU.MemSpeed = 8;
	CPU.MemSpeedx2 = 16;
#endif
	CPU.PCBase = Memory.RAM;
	CPU.PC = Memory.RAM + (Address & 0xffff);
	return;
    }
}
#endif
