/*
 * Project 64 - A Nintendo 64 emulator.
 *
 * (c) Copyright 2001 zilmar (zilmar@emulation64.com) and
 * Jabo (jabo@emulation64.com).
 *
 * pj64 homepage: www.pj64.net
 *
 * Permission to use, copy, modify and distribute Project64 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.
 *
 * Project64 is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Project64 or software derived from Project64.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so if they want them.
 *
 */
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "usf.h"
#include "main.h"
#include "cpu.h"
#include "audio.h"
#include "rsp.h"

#include "usf_internal.h"

uint8_t * PageROM(usf_state_t * state, uint32_t addr) {
	return (state->ROMPages[addr/0x10000])?state->ROMPages[addr/0x10000]+(addr%0x10000):&state->EmptySpace;
}

void * large_alloc(size_t);
void large_free(void *, size_t);

#ifdef _WIN32
#include <Windows.h>

void * large_alloc(size_t size)
{
	return VirtualAlloc( NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );
}

void large_free(void * p, size_t size)
{
	VirtualFree( p, size, MEM_RELEASE );
}
#else
#include <sys/mman.h>

#ifdef __APPLE__
#define MAP_ANONYMOUS MAP_ANON
#endif

void * large_alloc(size_t size)
{
	return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}

void large_free(void * p, size_t size)
{
	munmap( p, size );
}
#endif

int32_t Allocate_Memory ( void * state ) {
	//uint32_t i = 0;
	//RdramSize = 0x800000;

	// Allocate the N64MEM and TLB_Map so that they are in each others 4GB range
	// Also put the registers there :)


	// the mmap technique works craptacular when the regions don't overlay

	USF_STATE->MemChunk = (uint8_t *) large_alloc( 0x100000 * sizeof(uintptr_t) + 0x1D000 + USF_STATE->RdramSize );

	USF_STATE->TLB_Map = (uintptr_t*)USF_STATE->MemChunk;
	if (USF_STATE->TLB_Map == NULL) {
		return 0;
	}

	memset(USF_STATE->TLB_Map, 0, 0x100000 * sizeof(uintptr_t) + 0x10000);

	USF_STATE->N64MEM = USF_STATE->MemChunk + 0x100000 * sizeof(uintptr_t) + 0x10000;
	if(USF_STATE->N64MEM == NULL) {
		DisplayError(USF_STATE, "Failed to allocate N64MEM");
		return 0;
	}
	
	//memset(state->N64MEM, 0, USF_STATE->RdramSize);

	USF_STATE->NOMEM = USF_STATE->N64MEM + USF_STATE->RdramSize;
	
	//if(USF_STATE->RdramSize == 0x400000)
	//{
	//	munmap(N64MEM + 0x400000, 0x400000);
	//}

	USF_STATE->Registers = (N64_REGISTERS *)((uintptr_t)USF_STATE->MemChunk + 0x100000 * sizeof(uintptr_t));
	//USF_STATE->TLBLoadAddress = (uint32_t *)((uintptr_t)USF_STATE->Registers + 0x500);
	//USF_STATE->Timers = (SYSTEM_TIMERS*)(USF_STATE->TLBLoadAddress + 4);
	USF_STATE->Timers = (SYSTEM_TIMERS*)((uintptr_t)USF_STATE->Registers + 0x500);
	USF_STATE->WaitMode = (uint32_t *)(USF_STATE->Timers + sizeof(SYSTEM_TIMERS));
	USF_STATE->CPU_Action = (CPU_ACTION *)(USF_STATE->WaitMode + 4);
	//USF_STATE->RSP_GPR = (struct RSP_GPR_TYPE *)(USF_STATE->CPU_Action + sizeof(CPU_ACTION));
	//USF_STATE->DMEM = (uint8_t *)(USF_STATE->RSP_GPR + (32 * 16));
	USF_STATE->DMEM = (uint8_t *)(USF_STATE->CPU_Action + sizeof(CPU_ACTION));
	//state->RSP_ACCUM = (struct RSP_ACCUM_TYPE *)(USF_STATE->DMEM + 0x2000);

	USF_STATE->RDRAM = (uint8_t *)(USF_STATE->N64MEM);
	USF_STATE->IMEM  = USF_STATE->DMEM + 0x1000;

	USF_STATE->MemoryState = 1;

	return 1;
}

int PreAllocate_Memory(usf_state_t * state) {
	int i = 0;
	
	// Moved the savestate allocation here :)  (for better management later)
	state->savestatespace = (uint8_t *) malloc(0x80275C);
	
	if(state->savestatespace == 0)
		return 0;
	
	memset(state->savestatespace, 0, 0x80275C);
	
	for (i = 0; i < 0x400; i++) {
		state->ROMPages[i] = 0;
	}

	return 1;
}

void Release_Memory ( usf_state_t * state ) {
	uint32_t i;

	for (i = 0; i < 0x400; i++) {
		if (state->ROMPages[i]) {
			free(state->ROMPages[i]); state->ROMPages[i] = 0;
		}
	}
	//printf("Freeing memory\n");

	state->MemoryState = 0;

	if (state->MemChunk != 0) { large_free( state->MemChunk, 0x100000 * sizeof(uintptr_t) + 0x1D000 + state->RdramSize ); state->MemChunk=0; }

	if(state->cpu_hle_entries)
		free(state->cpu_hle_entries);
	state->cpu_hle_entries = NULL;

	if(state->savestatespace)
		free(state->savestatespace);
	state->savestatespace = NULL;
}


int32_t r4300i_LB_NonMemory ( usf_state_t * state, uint32_t PAddr, uint32_t * Value, uint32_t SignExtend ) {
	if (PAddr >= 0x10000000 && PAddr < 0x16000000) {
		if (state->WrittenToRom) { return 0; }
		if ((PAddr & 2) == 0) { PAddr = (PAddr + 4) ^ 2; }
		if ((PAddr - 0x10000000) < state->RomFileSize) {
			if (SignExtend) {
				*Value = (char)*PageROM(state, (PAddr - 0x10000000)^3);

			} else {
				*Value = *PageROM(state, (PAddr - 0x10000000)^3);
			}
			return 1;
		} else {
			*Value = 0;
			return 0;
		}
	}

	switch (PAddr & 0xFFF00000) {
	default:
		* Value = 0;
		return 0;
		break;
	}
	return 1;
}

uint32_t r4300i_LB_VAddr ( usf_state_t * state, uint32_t VAddr, uint8_t * Value ) {
	uintptr_t address;
	address = state->TLB_Map[VAddr >> 12];
	if (address == 0) { return 0; }
	*Value = *(uint8_t *)(address + (VAddr ^ 3));
	return 1;
}

uint32_t r4300i_LD_VAddr ( usf_state_t * state, uint32_t VAddr, uint64_t * Value ) {
	uintptr_t address;
	address = state->TLB_Map[VAddr >> 12];
	if (address == 0) { return 0; }
	if (address + VAddr + 7 - (uintptr_t)state->N64MEM >= state->RdramSize) {
		*((uint32_t *)(Value) + 1) = 0;
		*((uint32_t *)(Value)) = 0;
		return 1;
	}
	*((uint32_t *)(Value) + 1) = *(uint32_t *)(address + VAddr);
	*((uint32_t *)(Value)) = *(uint32_t *)(address + VAddr + 4);
	return 1;
}

int32_t r4300i_LH_NonMemory ( usf_state_t * state, uint32_t PAddr, uint32_t * Value, int32_t SignExtend ) {
	(void)state;
	(void)SignExtend;
	switch (PAddr & 0xFFF00000) {
	default:
		* Value = 0;
		return 0;
		break;
	}
	return 1;
}

uint32_t r4300i_LH_VAddr ( usf_state_t * state, uint32_t VAddr, uint16_t * Value ) {
	uintptr_t address;
	address = state->TLB_Map[VAddr >> 12];
	if (address == 0)
		return 0;
	if (address + (VAddr ^ 2) + 1 - (uintptr_t)state->N64MEM >= state->RdramSize) {
		*Value = 0;
		return 1;
	}
	*Value = *(uint16_t *)(address + (VAddr ^ 2));
	return 1;
}

int32_t r4300i_LW_NonMemory ( usf_state_t * state, uint32_t PAddr, uint32_t * Value ) {
	if (PAddr >= 0x10000000 && PAddr < 0x16000000) {
		if (state->WrittenToRom) {
			*Value = state->WroteToRom;
			//LogMessage("%X: Read crap from Rom %X from %X",PROGRAM_COUNTER,*Value,PAddr);
			state->WrittenToRom = 0;
			return 1;
		}
		if ((PAddr - 0x10000000) < state->RomFileSize) {
			*Value = *(uint32_t *)PageROM(state, (PAddr - 0x10000000));
			return 1;
		} else {
			*Value = PAddr & 0xFFFF;
			*Value = (*Value << 16) | *Value;
			return 0;
		}
	}

	switch (PAddr & 0xFFF00000) {
	case 0x03F00000:
		switch (PAddr) {
		case 0x03F00000: * Value = RDRAM_CONFIG_REG; break;
		case 0x03F00004: * Value = RDRAM_DEVICE_ID_REG; break;
		case 0x03F00008: * Value = RDRAM_DELAY_REG; break;
		case 0x03F0000C: * Value = RDRAM_MODE_REG; break;
		case 0x03F00010: * Value = RDRAM_REF_INTERVAL_REG; break;
		case 0x03F00014: * Value = RDRAM_REF_ROW_REG; break;
		case 0x03F00018: * Value = RDRAM_RAS_INTERVAL_REG; break;
		case 0x03F0001C: * Value = RDRAM_MIN_INTERVAL_REG; break;
		case 0x03F00020: * Value = RDRAM_ADDR_SELECT_REG; break;
		case 0x03F00024: * Value = RDRAM_DEVICE_MANUF_REG; break;
		default:
			* Value = 0;
			return 0;
		}
		break;
	case 0x04000000:
		switch (PAddr) {
		case 0x04040010: *Value = SP_STATUS_REG; break;
		case 0x04040014: *Value = SP_DMA_FULL_REG; break;
		case 0x04040018: *Value = SP_DMA_BUSY_REG; break;
		case 0x04080000: *Value = SP_PC_REG; break;
		default:
			* Value = 0;
			return 0;
		}
		break;
	case 0x04100000:
		switch (PAddr) {
		case 0x0410000C: *Value = DPC_STATUS_REG; break;
		case 0x04100010: *Value = DPC_CLOCK_REG; break;
		case 0x04100014: *Value = DPC_BUFBUSY_REG; break;
		case 0x04100018: *Value = DPC_PIPEBUSY_REG; break;
		case 0x0410001C: *Value = DPC_TMEM_REG; break;
		default:
			* Value = 0;
			return 0;
		}
		break;
	case 0x04300000:
		switch (PAddr) {
		case 0x04300000: * Value = MI_MODE_REG; break;
		case 0x04300004: * Value = MI_VERSION_REG; break;
		case 0x04300008: * Value = MI_INTR_REG; break;
		case 0x0430000C: * Value = MI_INTR_MASK_REG; break;
		default:
			* Value = 0;
			return 0;
		}
		break;
	case 0x04400000:
		switch (PAddr) {
		case 0x04400000: *Value = VI_STATUS_REG; break;
		case 0x04400004: *Value = VI_ORIGIN_REG; break;
		case 0x04400008: *Value = VI_WIDTH_REG; break;
		case 0x0440000C: *Value = VI_INTR_REG; break;
		case 0x04400010:
			*Value = 0;
			break;
		case 0x04400014: *Value = VI_BURST_REG; break;
		case 0x04400018: *Value = VI_V_SYNC_REG; break;
		case 0x0440001C: *Value = VI_H_SYNC_REG; break;
		case 0x04400020: *Value = VI_LEAP_REG; break;
		case 0x04400024: *Value = VI_H_START_REG; break;
		case 0x04400028: *Value = VI_V_START_REG ; break;
		case 0x0440002C: *Value = VI_V_BURST_REG; break;
		case 0x04400030: *Value = VI_X_SCALE_REG; break;
		case 0x04400034: *Value = VI_Y_SCALE_REG; break;
		default:
			* Value = 0;
			return 0;
		}
		break;
	case 0x04500000:
		switch (PAddr) {
		case 0x04500004: *Value = AiReadLength(state); break;
		case 0x0450000C: *Value = AI_STATUS_REG; break;
		default:
			* Value = 0;
			return 0;
		}
		break;
	case 0x04600000:
		switch (PAddr) {
		case 0x04600010: *Value = PI_STATUS_REG; break;
		case 0x04600014: *Value = PI_DOMAIN1_REG; break;
		case 0x04600018: *Value = PI_BSD_DOM1_PWD_REG; break;
		case 0x0460001C: *Value = PI_BSD_DOM1_PGS_REG; break;
		case 0x04600020: *Value = PI_BSD_DOM1_RLS_REG; break;
		case 0x04600024: *Value = PI_DOMAIN2_REG; break;
		case 0x04600028: *Value = PI_BSD_DOM2_PWD_REG; break;
		case 0x0460002C: *Value = PI_BSD_DOM2_PGS_REG; break;
		case 0x04600030: *Value = PI_BSD_DOM2_RLS_REG; break;
		default:
			* Value = 0;
			return 0;
		}
		break;
	case 0x04700000:
		switch (PAddr) {
		case 0x04700000: * Value = RI_MODE_REG; break;
		case 0x04700004: * Value = RI_CONFIG_REG; break;
		case 0x04700008: * Value = RI_CURRENT_LOAD_REG; break;
		case 0x0470000C: * Value = RI_SELECT_REG; break;
		case 0x04700010: * Value = RI_REFRESH_REG; break;
		case 0x04700014: * Value = RI_LATENCY_REG; break;
		case 0x04700018: * Value = RI_RERROR_REG; break;
		case 0x0470001C: * Value = RI_WERROR_REG; break;
		default:
			* Value = 0;
			return 0;
		}
		break;
	case 0x04800000:
		switch (PAddr) {
		case 0x04800018: *Value = SI_STATUS_REG; break;
		default:
			*Value = 0;
			return 0;
		}
		break;
	case 0x05000000:
		*Value = PAddr & 0xFFFF;
		*Value = (*Value << 16) | *Value;
		return 0;
	case 0x08000000:
		*Value = 0;
		break;
	default:
		*Value = PAddr & 0xFFFF;
		*Value = (*Value << 16) | *Value;
		return 0;
		break;
	}
	return 1;
}

void r4300i_LW_PAddr ( usf_state_t * state, uint32_t PAddr, uint32_t * Value ) {
	*Value = *(uint32_t *)(state->N64MEM+PAddr);
}

uint32_t r4300i_LW_VAddr ( usf_state_t * state, uint32_t VAddr, uint32_t * Value ) {
	uintptr_t address = state->TLB_Map[VAddr >> 12];
	if (address == 0)
		return 0;

	address += VAddr;

	if((address - (uintptr_t)state->RDRAM) > state->RdramSize) {
		address = address - (uintptr_t)state->RDRAM;
		return r4300i_LW_NonMemory(state, (uint32_t) address, Value);
	}
	*Value = *(uint32_t *)address;
	return 1;
}

int32_t r4300i_SB_NonMemory ( usf_state_t * state, uint32_t PAddr, uint8_t Value ) {
	switch (PAddr & 0xFFF00000) {
	case 0x00000000:
	case 0x00100000:
	case 0x00200000:
	case 0x00300000:
	case 0x00400000:
	case 0x00500000:
	case 0x00600000:
	case 0x00700000:
		if (PAddr < state->RdramSize) {
			*(uint8_t *)(state->N64MEM+PAddr) = Value;
		}
		break;
	default:
		return 0;
		break;
	}
	return 1;
}

uint32_t r4300i_SB_VAddr ( usf_state_t * state, uint32_t VAddr, uint8_t Value ) {
	uintptr_t address;
	address = state->TLB_Map[VAddr >> 12];

	if (address == 0) { return 0; }
	if (address + (VAddr ^ 3) - (uintptr_t)state->N64MEM < state->RdramSize)
		*(uint8_t *)(address + (VAddr ^ 3)) = Value;

	return 1;
}

int32_t r4300i_SH_NonMemory ( usf_state_t * state, uint32_t PAddr, uint16_t Value ) {
	switch (PAddr & 0xFFF00000) {
	case 0x00000000:
	case 0x00100000:
	case 0x00200000:
	case 0x00300000:
	case 0x00400000:
	case 0x00500000:
	case 0x00600000:
	case 0x00700000:
		if (PAddr < state->RdramSize) {
			*(uint16_t *)(state->N64MEM+PAddr) = Value;
		}
		break;
	default:
		return 0;
		break;
	}
	return 1;
}

uint32_t r4300i_SD_VAddr ( usf_state_t * state, uint32_t VAddr, uint64_t Value ) {
	uintptr_t address;
	address = state->TLB_Map[VAddr >> 12];
	if (address == 0) { return 0; }
	if (address + VAddr + 7 - (uintptr_t)state->N64MEM < state->RdramSize) {
		*(uint32_t *)(address + VAddr) = *((uint32_t *)(&Value) + 1);
		*(uint32_t *)(address + VAddr + 4) = *((uint32_t *)(&Value));
	}
	return 1;
}

uint32_t r4300i_SH_VAddr ( usf_state_t * state, uint32_t VAddr, uint16_t Value ) {
	uintptr_t address;
	address = state->TLB_Map[VAddr >> 12];

	if (address == 0) { return 0; }
	if (address + 1 + (VAddr ^ 2) - (uintptr_t)state->N64MEM < state->RdramSize)
		*(uint16_t *)(address + (VAddr ^ 2)) = Value;
	return 1;
}

int32_t r4300i_SW_NonMemory ( usf_state_t * state, uint32_t PAddr, uint32_t Value ) {
	if (PAddr >= 0x10000000 && PAddr < 0x16000000) {
		if ((PAddr - 0x10000000) < state->RomFileSize) {
			state->WrittenToRom = 1;
			state->WroteToRom = Value;
		} else {
			return 0;
		}
	}

	switch (PAddr & 0xFFF00000) {
	case 0x00000000:
	case 0x00100000:
	case 0x00200000:
	case 0x00300000:
	case 0x00400000:
	case 0x00500000:
	case 0x00600000:
	case 0x00700000:
		if (PAddr < state->RdramSize) {
			*(uint32_t *)(state->N64MEM+PAddr) = Value;
		}
		break;
	case 0x03F00000:
		switch (PAddr) {
		case 0x03F00000: RDRAM_CONFIG_REG = Value; break;
		case 0x03F00004: RDRAM_DEVICE_ID_REG = Value; break;
		case 0x03F00008: RDRAM_DELAY_REG = Value; break;
		case 0x03F0000C: RDRAM_MODE_REG = Value; break;
		case 0x03F00010: RDRAM_REF_INTERVAL_REG = Value; break;
		case 0x03F00014: RDRAM_REF_ROW_REG = Value; break;
		case 0x03F00018: RDRAM_RAS_INTERVAL_REG = Value; break;
		case 0x03F0001C: RDRAM_MIN_INTERVAL_REG = Value; break;
		case 0x03F00020: RDRAM_ADDR_SELECT_REG = Value; break;
		case 0x03F00024: RDRAM_DEVICE_MANUF_REG = Value; break;
		case 0x03F04004: break;
		case 0x03F08004: break;
		case 0x03F80004: break;
		case 0x03F80008: break;
		case 0x03F8000C: break;
		case 0x03F80014: break;
		default:
			return 0;
		}
		break;
	case 0x04000000:
		if (PAddr < 0x04002000) {
			*(uint32_t *)(state->N64MEM+PAddr) = Value;
			return 1;
		}
		switch (PAddr) {
		case 0x04040000: SP_MEM_ADDR_REG = Value; break;
		case 0x04040004: SP_DRAM_ADDR_REG = Value; break;
		case 0x04040008:
			SP_RD_LEN_REG = Value;
			SP_DMA_READ(state);
			break;
		case 0x0404000C:
			SP_WR_LEN_REG = Value;
			SP_DMA_WRITE(state);
			break;
		case 0x04040010:
			if ( ( Value & SP_CLR_HALT ) != 0) { SP_STATUS_REG &= ~SP_STATUS_HALT; }
			if ( ( Value & SP_SET_HALT ) != 0) { SP_STATUS_REG |= SP_STATUS_HALT;  }
			if ( ( Value & SP_CLR_BROKE ) != 0) { SP_STATUS_REG &= ~SP_STATUS_BROKE; }
			if ( ( Value & SP_CLR_INTR ) != 0) {
				MI_INTR_REG &= ~MI_INTR_SP;
				CheckInterrupts(state);
			}
			if ( ( Value & SP_CLR_SSTEP ) != 0) { SP_STATUS_REG &= ~SP_STATUS_SSTEP; }
			if ( ( Value & SP_SET_SSTEP ) != 0) { SP_STATUS_REG |= SP_STATUS_SSTEP;  }
			if ( ( Value & SP_CLR_INTR_BREAK ) != 0) { SP_STATUS_REG &= ~SP_STATUS_INTR_BREAK; }
			if ( ( Value & SP_SET_INTR_BREAK ) != 0) { SP_STATUS_REG |= SP_STATUS_INTR_BREAK;  }
			if ( ( Value & SP_CLR_SIG0 ) != 0) { SP_STATUS_REG &= ~SP_STATUS_SIG0; }
			if ( ( Value & SP_SET_SIG0 ) != 0) { SP_STATUS_REG |= SP_STATUS_SIG0;  }
			if ( ( Value & SP_CLR_SIG1 ) != 0) { SP_STATUS_REG &= ~SP_STATUS_SIG1; }
			if ( ( Value & SP_SET_SIG1 ) != 0) { SP_STATUS_REG |= SP_STATUS_SIG1;  }
			if ( ( Value & SP_CLR_SIG2 ) != 0) { SP_STATUS_REG &= ~SP_STATUS_SIG2; }
			if ( ( Value & SP_SET_SIG2 ) != 0) { SP_STATUS_REG |= SP_STATUS_SIG2;  }
			if ( ( Value & SP_CLR_SIG3 ) != 0) { SP_STATUS_REG &= ~SP_STATUS_SIG3; }
			if ( ( Value & SP_SET_SIG3 ) != 0) { SP_STATUS_REG |= SP_STATUS_SIG3;  }
			if ( ( Value & SP_CLR_SIG4 ) != 0) { SP_STATUS_REG &= ~SP_STATUS_SIG4; }
			if ( ( Value & SP_SET_SIG4 ) != 0) { SP_STATUS_REG |= SP_STATUS_SIG4;  }
			if ( ( Value & SP_CLR_SIG5 ) != 0) { SP_STATUS_REG &= ~SP_STATUS_SIG5; }
			if ( ( Value & SP_SET_SIG5 ) != 0) { SP_STATUS_REG |= SP_STATUS_SIG5;  }
			if ( ( Value & SP_CLR_SIG6 ) != 0) { SP_STATUS_REG &= ~SP_STATUS_SIG6; }
			if ( ( Value & SP_SET_SIG6 ) != 0) { SP_STATUS_REG |= SP_STATUS_SIG6;  }
			if ( ( Value & SP_CLR_SIG7 ) != 0) { SP_STATUS_REG &= ~SP_STATUS_SIG7; }
			if ( ( Value & SP_SET_SIG7 ) != 0) { SP_STATUS_REG |= SP_STATUS_SIG7;  }

			RunRsp(state);

			break;
		case 0x0404001C: SP_SEMAPHORE_REG = 0; break;
		case 0x04080000: SP_PC_REG = Value & 0xFFC; break;
		default:
			return 0;
		}
		break;
	case 0x04100000:
		switch (PAddr) {
		case 0x04100000:
			DPC_START_REG = Value;
			DPC_CURRENT_REG = Value;
			break;
		case 0x04100004:
			DPC_END_REG = Value;
			break;
		case 0x04100008: DPC_CURRENT_REG = Value; break;
		case 0x0410000C:
			if ( ( Value & DPC_CLR_XBUS_DMEM_DMA ) != 0) { DPC_STATUS_REG &= ~DPC_STATUS_XBUS_DMEM_DMA; }
			if ( ( Value & DPC_SET_XBUS_DMEM_DMA ) != 0) { DPC_STATUS_REG |= DPC_STATUS_XBUS_DMEM_DMA;  }
			if ( ( Value & DPC_CLR_FREEZE ) != 0) { DPC_STATUS_REG &= ~DPC_STATUS_FREEZE; }
			if ( ( Value & DPC_SET_FREEZE ) != 0) { DPC_STATUS_REG |= DPC_STATUS_FREEZE;  }
			if ( ( Value & DPC_CLR_FLUSH ) != 0) { DPC_STATUS_REG &= ~DPC_STATUS_FLUSH; }
			if ( ( Value & DPC_SET_FLUSH ) != 0) { DPC_STATUS_REG |= DPC_STATUS_FLUSH;  }
			if ( ( Value & DPC_CLR_FREEZE ) != 0)
			{
				if ( ( SP_STATUS_REG & SP_STATUS_HALT ) == 0)
				{
					if ( ( SP_STATUS_REG & SP_STATUS_BROKE ) == 0 )
					{
						RunRsp(state);
					}
				}
			}
			break;
		default:
			return 0;
		}
		break;
	case 0x04300000:
		switch (PAddr) {
		case 0x04300000:
			MI_MODE_REG &= ~0x7F;
			MI_MODE_REG |= (Value & 0x7F);
			if ( ( Value & MI_CLR_INIT ) != 0 ) { MI_MODE_REG &= ~MI_MODE_INIT; }
			if ( ( Value & MI_SET_INIT ) != 0 ) { MI_MODE_REG |= MI_MODE_INIT; }
			if ( ( Value & MI_CLR_EBUS ) != 0 ) { MI_MODE_REG &= ~MI_MODE_EBUS; }
			if ( ( Value & MI_SET_EBUS ) != 0 ) { MI_MODE_REG |= MI_MODE_EBUS; }
			if ( ( Value & MI_CLR_DP_INTR ) != 0 ) {
				MI_INTR_REG &= ~MI_INTR_DP;
				CheckInterrupts(state);
			}
			if ( ( Value & MI_CLR_RDRAM ) != 0 ) { MI_MODE_REG &= ~MI_MODE_RDRAM; }
			if ( ( Value & MI_SET_RDRAM ) != 0 ) { MI_MODE_REG |= MI_MODE_RDRAM; }
			break;
		case 0x0430000C:
			if ( ( Value & MI_INTR_MASK_CLR_SP ) != 0 ) { MI_INTR_MASK_REG &= ~MI_INTR_MASK_SP; }
			if ( ( Value & MI_INTR_MASK_SET_SP ) != 0 ) { MI_INTR_MASK_REG |= MI_INTR_MASK_SP; }
			if ( ( Value & MI_INTR_MASK_CLR_SI ) != 0 ) { MI_INTR_MASK_REG &= ~MI_INTR_MASK_SI; }
			if ( ( Value & MI_INTR_MASK_SET_SI ) != 0 ) { MI_INTR_MASK_REG |= MI_INTR_MASK_SI; }
			if ( ( Value & MI_INTR_MASK_CLR_AI ) != 0 ) { MI_INTR_MASK_REG &= ~MI_INTR_MASK_AI; }
			if ( ( Value & MI_INTR_MASK_SET_AI ) != 0 ) { MI_INTR_MASK_REG |= MI_INTR_MASK_AI; }
			if ( ( Value & MI_INTR_MASK_CLR_VI ) != 0 ) { MI_INTR_MASK_REG &= ~MI_INTR_MASK_VI; }
			if ( ( Value & MI_INTR_MASK_SET_VI ) != 0 ) { MI_INTR_MASK_REG |= MI_INTR_MASK_VI; }
			if ( ( Value & MI_INTR_MASK_CLR_PI ) != 0 ) { MI_INTR_MASK_REG &= ~MI_INTR_MASK_PI; }
			if ( ( Value & MI_INTR_MASK_SET_PI ) != 0 ) { MI_INTR_MASK_REG |= MI_INTR_MASK_PI; }
			if ( ( Value & MI_INTR_MASK_CLR_DP ) != 0 ) { MI_INTR_MASK_REG &= ~MI_INTR_MASK_DP; }
			if ( ( Value & MI_INTR_MASK_SET_DP ) != 0 ) { MI_INTR_MASK_REG |= MI_INTR_MASK_DP; }
			break;
		default:
			return 0;
		}
		break;
	case 0x04400000:
		switch (PAddr) {
		case 0x04400000:
			//if (VI_STATUS_REG != Value) {
				VI_STATUS_REG = Value;
			//	if (ViStatusChanged != NULL ) { ViStatusChanged(); }
			//}
			break;
		case 0x04400004:

			VI_ORIGIN_REG = (Value & 0xFFFFFF);
			//if (UpdateScreen != NULL ) { UpdateScreen(); }
			break;
		case 0x04400008:
			//if (VI_WIDTH_REG != Value) {
				VI_WIDTH_REG = Value;
			//	if (ViWidthChanged != NULL ) { ViWidthChanged(); }
			//}
			break;
		case 0x0440000C: VI_INTR_REG = Value; break;
		case 0x04400010:
			MI_INTR_REG &= ~MI_INTR_VI;
			CheckInterrupts(state);
			break;
		case 0x04400014: VI_BURST_REG = Value; break;
		case 0x04400018: VI_V_SYNC_REG = Value; break;
		case 0x0440001C: VI_H_SYNC_REG = Value; break;
		case 0x04400020: VI_LEAP_REG = Value; break;
		case 0x04400024: VI_H_START_REG = Value; break;
		case 0x04400028: VI_V_START_REG = Value; break;
		case 0x0440002C: VI_V_BURST_REG = Value; break;
		case 0x04400030: VI_X_SCALE_REG = Value; break;
		case 0x04400034: VI_Y_SCALE_REG = Value; break;
		default:
			return 0;
		}
		break;
	case 0x04500000:
		switch (PAddr) {
		case 0x04500000: AI_DRAM_ADDR_REG = Value; break;
		case 0x04500004:
			AI_LEN_REG = Value;
			AiLenChanged(state);
			break;
		case 0x04500008: AI_CONTROL_REG = (Value & 0x1); break;
		case 0x0450000C:
			/* Clear Interrupt */;
			MI_INTR_REG &= ~MI_INTR_AI;
			state->AudioIntrReg &= ~MI_INTR_AI;
			CheckInterrupts(state);
			break;
		case 0x04500010:
			AiDacrateChanged(state, Value);
			break;
		case 0x04500014:  AI_BITRATE_REG = Value; break;
		default:
			return 0;
		}
		break;
	case 0x04600000:
		switch (PAddr) {
		case 0x04600000: PI_DRAM_ADDR_REG = Value; break;
		case 0x04600004: PI_CART_ADDR_REG = Value; break;
		case 0x04600008:
			PI_RD_LEN_REG = Value;
			PI_DMA_READ(state);
			break;
		case 0x0460000C:
			PI_WR_LEN_REG = Value;
			PI_DMA_WRITE(state);
			break;
		case 0x04600010:
			//if ((Value & PI_SET_RESET) != 0 ) { DisplayError(state, "reset Controller"); }
			if ((Value & PI_CLR_INTR) != 0 ) {
				MI_INTR_REG &= ~MI_INTR_PI;
				CheckInterrupts(state);
			}
			break;
		case 0x04600014: PI_DOMAIN1_REG = (Value & 0xFF); break;
		case 0x04600018: PI_BSD_DOM1_PWD_REG = (Value & 0xFF); break;
		case 0x0460001C: PI_BSD_DOM1_PGS_REG = (Value & 0xFF); break;
		case 0x04600020: PI_BSD_DOM1_RLS_REG = (Value & 0xFF); break;
		default:
			return 0;
		}
		break;
	case 0x04700000:
		switch (PAddr) {
		case 0x04700000: RI_MODE_REG = Value; break;
		case 0x04700004: RI_CONFIG_REG = Value; break;
		case 0x04700008: RI_CURRENT_LOAD_REG = Value; break;
		case 0x0470000C: RI_SELECT_REG = Value; break;
		case 0x04700010: RI_REFRESH_REG = Value; break;
		case 0x04700014: RI_LATENCY_REG = Value; break;
		case 0x04700018: RI_RERROR_REG = Value; break;
		case 0x0470001C: RI_WERROR_REG = Value; break;
		default:
			return 0;
		}
		break;
	case 0x04800000:
		switch (PAddr) {
		case 0x04800000: SI_DRAM_ADDR_REG = Value; break;
		case 0x04800004:
			SI_PIF_ADDR_RD64B_REG = Value;
			SI_DMA_READ (state);
			break;
		case 0x04800010:
			SI_PIF_ADDR_WR64B_REG = Value;
			SI_DMA_WRITE(state);
			break;
		case 0x04800018:
			MI_INTR_REG &= ~MI_INTR_SI;
			SI_STATUS_REG &= ~SI_STATUS_INTERRUPT;
			CheckInterrupts(state);
			break;
		default:
			return 0;
		}
		break;
	case 0x08000000:
		if (PAddr != 0x08010000) { return 0; }
		break;
	case 0x1FC00000:
		if (PAddr < 0x1FC007C0) {
			return 0;
		} else if (PAddr < 0x1FC00800) {

			if (PAddr == 0x1FC007FC) {
				PifRamWrite(state);
			}
			return 1;
		}
		return 0;
		break;
	default:
		return 0;
		break;
	}
	return 1;
}

uint32_t r4300i_SW_VAddr ( usf_state_t * state, uint32_t VAddr, uint32_t Value ) {

	uintptr_t address;
	address = state->TLB_Map[VAddr >> 12];

	if (address == 0) { return 0; }
	address = (address + VAddr);
	
	if((address - (uintptr_t)state->RDRAM) > state->RdramSize) {
		address = address - (uintptr_t)state->RDRAM;
		return r4300i_SW_NonMemory(state, (uint32_t) address, Value);
	}
	*(uint32_t *)address = Value;
	return 1;
}

void memcpyn642n64(usf_state_t * state, uint32_t dest, uint32_t src, uint32_t len)
{
	uint32_t i;
	uint32_t temp;

	for (i = 0; i < len; i += 4) {
		uintptr_t dstAddr = state->TLB_Map[(dest + i) >> 12];
		uintptr_t srcAddr = state->TLB_Map[(src + i) >> 12];

		if (srcAddr)
			temp = *(uint32_t*)(srcAddr + src + i);
		else
			temp = 0;

		if (dstAddr)
			*(uint32_t*)(dstAddr + dest + i) = temp;
	}
}
