//
// 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: mmio.cpp $
// Revision 1.14  1998/06/18 21:36:51  BREAKPOINT
// Added interrupt regeneration when RSP INTR MASK is re-enabled.
// Revision 1.13  1998/05/24 05:58:19  BREAKPOINT
// Clamped RDP display list to RSP DMEM.
// Revision 1.12  1998/05/23 15:41:24  BREAKPOINT
// Implemented new (and correct) interrupt system..
// Revision 1.11  1998/05/14 01:27:25  BREAKPOINT
// Added RDP emulation.
// Revision 1.10  1998/05/03 01:04:25  BREAKPOINT
// Fixed RSP startup code to not be dependant on 125h written to STATUS reg.
// Revision 1.9  1998/04/23 23:35:47  BREAKPOINT
// Began RDP CMD MMIO register emulation.
// Revision 1.8  1998/04/05 17:00:59  LEGERDE
// added SP_IBIST and SP_SEMAPHORE handlers
// Revision 1.7  1998/04/05 01:33:42  BREAKPOINT
// Added modulus to VI_CURR_LINE register to be max of 512.
// Revision 1.6  1998/04/04 16:39:51  BREAKPOINT
// Made RSP processor a pointer to RSP class.
// Revision 1.5  1998/04/02 21:18:20  BREAKPOINT
// Implemented new memory mapping routines and changed ROM loading code.
// Revision 1.4  1998/03/22 17:54:02  BREAKPOINT
// New RSP stuff!
// Revision 1.3  1998/03/08 19:12:23  BREAKPOINT
// Version 0.3b.3 checkin... I need to really check this stuff in more often..
// Revision 1.2  1998/01/31 19:52:27  BREAKPOINT
// New code to handle new assembly data type instead of u64.
// Revision 1.1  1998/01/10 01:27:58  BREAKPOINT
// Initial revision
//

static char *rcsid = "$Id: mmio.cpp 1.14 1998/06/18 21:36:51 BREAKPOINT Exp $";

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

#include <types.h>
#include "unreal.h"
#include "emu.h"
#include "heap.h"
#include "mmio.h"
#include "debugger.h"
#include "cfb.h"
#include "joy.h"
#include "audio.h"
#include "heap.h"
#include "rsp.h"
#include "rdp.h"

// Include inline functions...
#include "inline.cpp"

void InitLog(void)
{
    FILE *fp;

    if (!log_mmio_reg) return;

    fp = fopen("mmio.log", "r+t");
    if (fp == NULL) fp = fopen("mmio.log", "wt");
    fseek(fp, 0, 2);
    fprintf(fp, "\n *** %s loaded ***\n", v64h.Filename);
    fclose(fp);
}

// Logging function to see what's being read from/written to...
void Log(int t, char *txt, ulong val)
{    
    FILE *fp;

    if (!log_mmio_reg) return;

    fp = fopen("mmio.log", "r+t");
    if (fp == NULL) fp = fopen("mmio.log", "wt");
    fseek(fp, 0, 2);
    fprintf(fp, "%08X: %s %s (%08X)\n", r4reg.PC, t == TYPE_READ ? "Read from" : "Write to", txt, val);
    fclose(fp);
}

void MMIO_DoNothing_Read(void)
{
}

void MMIO_DoNothing_Write(void)
{
}

void RDRAM_Temp_Read(void)
{
	char buf[64];

	sprintf(buf, "RDRAM UNKNOWN MEMORY (addr %08X)", MMIO_offs + RDRAM_BASE_REG + RDRAM_BASE_SIZE);
	Log(TYPE_READ, buf, 0);
}

void RDRAM_Temp_Write(void)
{
	char buf[64];

	sprintf(buf, "RDRAM UNKNOWN MEMORY (addr %08X)", MMIO_offs + RDRAM_BASE_REG + RDRAM_BASE_SIZE);
	Log(TYPE_WRITE, buf, MMIO_val[0]);
}

// *******************************************************************************************************
// *******************************************************************************************************
//
//                                             READ MMIO CALLS
//
// *******************************************************************************************************
// *******************************************************************************************************

//
// -------------------- RDRAM Read Reg --------------------
//

void RDRAM_RegRead_CONFIG(void) 
{
	Log(TYPE_READ, "RDRAM_CONFIG_REG / RDRAM_DEVICE_TYPE_REG", rdreg.config_devty);
}

void RDRAM_RegRead_DEVICE_ID(void) 
{
	Log(TYPE_READ, "RDRAM_DEVICE_ID_REG", rdreg.device_id);
}

void RDRAM_RegRead_DELAY(void) 
{
	Log(TYPE_READ, "RDRAM_DELAY_REG", rdreg.delay);
}

void RDRAM_RegRead_MODE(void) 
{
	Log(TYPE_READ, "RDRAM_MODE_REG", rdreg.mode);
}

void RDRAM_RegRead_REF_INTERVAL(void) 
{
	Log(TYPE_READ, "RDRAM_REF_INTERVAL_REG", rdreg.ref_interval);
}

void RDRAM_RegRead_REF_ROW(void)
{
	Log(TYPE_READ, "RDRAM_REF_ROW_REG", rdreg.ref_row);
}

void RDRAM_RegRead_RAS_INTERVAL(void) 
{
	Log(TYPE_READ, "RDRAM_RAS_INTERVAL_REG", rdreg.ras_interval);
}

void RDRAM_RegRead_MIN_INTERVAL(void) 
{
    Log(TYPE_READ, "RDRAM_MIN_INTERVAL_REG", rdreg.min_interval);
}

void RDRAM_RegRead_ADDR_SELECT(void) 
{
    Log(TYPE_READ, "RDRAM_ADDR_SELECT_REG", rdreg.addr_select);
}

void RDRAM_RegRead_DEVICE_MANUF(void) 
{
	Log(TYPE_READ, "RDRAM_DEVICE_MANUF_REG", rdreg.device_manuf);
}

void (*RDRAM_RegReadTable[10])(void) = {
    RDRAM_RegRead_CONFIG, RDRAM_RegRead_DEVICE_ID, RDRAM_RegRead_DELAY,
	RDRAM_RegRead_MODE, RDRAM_RegRead_REF_INTERVAL,RDRAM_RegRead_REF_ROW,
	RDRAM_RegRead_RAS_INTERVAL,RDRAM_RegRead_MIN_INTERVAL,
	RDRAM_RegRead_ADDR_SELECT,RDRAM_RegRead_DEVICE_MANUF
};

void RDRAM_Reg_Read(void)
{
    RDRAM_RegReadTable[MMIO_offs>>2]();
}

//
// -------------------- SP Read Reg --------------------
//

void SP_RegRead_MEM_ADDR(void)	// Master
{
	Log(TYPE_READ, "SP_MEM_ADDR_REG", spreg.mem_addr);
}

void SP_RegRead_DRAM_ADDR(void)	// Slave
{
	Log(TYPE_READ, "SP_DRAM_ADDR_REG", spreg.dram_addr);
}

void SP_RegRead_RD_LEN(void)	// R/W: read len
{
	Log(TYPE_READ, "SP_RD_LEN_REG", spreg.read_len);
}

void SP_RegRead_WR_LEN(void)	// R/W: write len
{
	Log(TYPE_READ, "SP_WR_LEN_REG", spreg.write_len);
}

void SP_RegRead_STATUS(void)
{
    Log(TYPE_READ, "SP_STATUS_REG", spreg.status);
    Write32(SP_BASE_REG + SP_STATUS_REG, spreg.status);
}

void SP_RegRead_DMA_FULL(void)
{
	Log(TYPE_READ, "SP_DMA_FULL_REG", spreg.dma_full);
}

void SP_RegRead_DMA_BUSY(void) 
{
	Log(TYPE_READ, "SP_DMA_BUSY_REG", spreg.dma_busy);
}

void SP_RegRead_SEMAPHORE(void)
{
	spreg.semaphore = 1;	// set semaphore on read
	Log(TYPE_READ, "SP_SEMAPHORE_REG", spreg.semaphore);
}

void (*SP_RegReadTable[8])(void) = {
	SP_RegRead_MEM_ADDR, SP_RegRead_DRAM_ADDR, SP_RegRead_RD_LEN,
	SP_RegRead_WR_LEN, SP_RegRead_STATUS, SP_RegRead_DMA_FULL,
	SP_RegRead_DMA_BUSY, SP_RegRead_SEMAPHORE
};

void SP_Reg_Read(void)
{
	SP_RegReadTable[MMIO_offs>>2]();
}

//
// -------------------- SP PC Read Reg --------------------
//

void SP_PC_Read(void)
{
    Log(TYPE_READ, "SP_PC_REG", sp_pc.PC);
}

//
// -------------------- SP IBIST Read Reg --------------------
//

void SP_IBIST_Read(void)
{
    Log(TYPE_READ, "SP_IBIST_REG", sp_ibist.ibist);
}

//
// -------------------- DPC Read Reg --------------------
//

void DPC_RegRead_START(void)
{
	Log(TYPE_READ, "DPC_START_REG", dpcreg.start);
}

void DPC_RegRead_END(void)
{
	Log(TYPE_READ, "DPC_END_REG", dpcreg.end);
}

void DPC_RegRead_CURRENT(void)
{
	Log(TYPE_READ, "DPC_CURRENT_REG", dpcreg.current);
}

void DPC_RegRead_STATUS(void)
{
	Log(TYPE_READ, "DPC_STATUS_REG", dpcreg.status);
}

void DPC_RegRead_CLOCK(void)
{
	Log(TYPE_READ, "DPC_CLOCK_REG", dpcreg.clock);
}

void DPC_RegRead_BUFBUSY(void)
{
	Log(TYPE_READ, "DPC_BUFBUSY_REG", dpcreg.bufbusy);
}

void DPC_RegRead_PIPEBUSY(void)
{
	Log(TYPE_READ, "DPC_PIPEBUSY_REG", dpcreg.pipebusy);
}

void DPC_RegRead_TMEM(void)
{
	Log(TYPE_READ, "DPC_TMEM_REG", dpcreg.tmem);
}

void (*DPC_RegReadTable[8])(void) = {
	DPC_RegRead_START, DPC_RegRead_END, DPC_RegRead_CURRENT,
	DPC_RegRead_STATUS, DPC_RegRead_CLOCK, DPC_RegRead_BUFBUSY,
	DPC_RegRead_PIPEBUSY, DPC_RegRead_TMEM
};

void DPC_Reg_Read(void)
{
	DPC_RegReadTable[MMIO_offs>>2]();
}

//
// -------------------- DPS Read Reg --------------------
//

void DPS_Reg_Read(void)
{
    switch (MMIO_offs)
    {
        case DPS_TBIST_REG:
            Log(TYPE_READ, "DPS_TBIST_REG", dpsreg.tbist);
            break;

        case DPS_TEST_MODE_REG:
            Log(TYPE_READ, "DPS_TEST_MODE_REG", dpsreg.test_mode);
            break;

        case DPS_BUFTEST_ADDR_REG:
            Log(TYPE_READ, "DPS_BUFTEST_ADDR_REG", dpsreg.buftest_addr);
            break;

        case DPS_BUFTEST_DATA_REG:
            Log(TYPE_READ, "DPS_BUFTEST_DATA_REG", dpsreg.buftest_data);
            break;
    }
}

//
// -------------------- MI Read Reg --------------------
//

void MI_Reg_Read(void)
{
    switch (MMIO_offs)
    {
        case MI_INIT_MODE_REG:
            Log(TYPE_READ, "MI_INIT_MODE_REG", mireg.mode);
			Write32(MI_BASE_REG + MI_INIT_MODE_REG, mireg.mode);
            break;

        case MI_VERSION_REG:
            Log(TYPE_READ, "MI_VERSION_REG", mireg.version);
			Write32(MI_BASE_REG + MI_VERSION_REG, mireg.version);
            break;

        case MI_INTR_REG:
            Log(TYPE_READ, "MI_INTR_REG", mireg.intr);
			Write32(MI_BASE_REG + MI_INTR_REG, mireg.intr);
            break;

        case MI_INTR_MASK_REG:
            Log(TYPE_READ, "MI_INTR_MASK_REG", mireg.intr_mask);
			Write32(MI_BASE_REG + MI_INTR_MASK_REG, mireg.intr_mask);
            break;
    }
}

//
// -------------------- VI Read Reg --------------------
//

void VI_RegRead_STATUS(void)
{
	Log(TYPE_READ, "VI_STATUS_REG / VI_CONTROL_REG", vireg.stat_ctrl);
	Write32(VI_BASE_REG + VI_STATUS_REG, vireg.stat_ctrl);
}

void VI_RegRead_ORIGIN(void)
{
	Log(TYPE_READ, "VI_ORIGIN_REG / VI_DRAM_ADDR_REG", vireg.origin);
	Write32(VI_BASE_REG + VI_ORIGIN_REG, vireg.origin);
}

void VI_RegRead_WIDTH(void)
{
	Log(TYPE_READ, "VI_WIDTH_REG / VI_H_WIDTH_REG", vireg.width);
	Write32(VI_BASE_REG + VI_WIDTH_REG, vireg.width);
}

void VI_RegRead_INTR(void)
{
	Log(TYPE_READ, "VI_INTR_REG / VI_V_INTR_REG", vireg.intr);
}

void VI_RegRead_CURRENT(void)
{
	static __int64 last_request_count = 0;
	static __int64 time_since_last_request = 0;

	// probably looping waiting for register to move..
	if (c0reg.Count - last_request_count < 10)
	{
		//we want to get Count to the next horizontal line or next interrupt.
		c0reg.Count += (__int64)(VERTICAL_RETRACE_TIME / 512);

        // check if an interrupt would have occurred earlier.
		if (c0reg.Count > N64EventArray[1].Time)
			c0reg.Count = N64EventArray[1].Time;
	}

	// Last Time for vertical retrace = Last_Vert_Retrace_Count
	// Number of Lines = CFB_Ysize
	// Current Time = c0reg.count
	// Total time between vertical retraces = VERT_RETRACE_TIME

    vireg.curr_vline = (ulong)(512.0 * ((float)(c0reg.Count - Last_Vert_Retrace_Count) / (float)VERTICAL_RETRACE_TIME));
	vireg.curr_vline %= 512;

    Log(TYPE_READ, "VI_CURRENT_REG / VI_V_CURRENT_LINE_REG", vireg.curr_vline);
	Write32(VI_BASE_REG + VI_CURRENT_REG, vireg.curr_vline);
	
	// store current time so we can determine how quickly we are being called.
	last_request_count = c0reg.Count;
}

void VI_RegRead_BURST(void)
{
	Log(TYPE_READ, "VI_BURST_REG / VI_TIMING_REG", vireg.vid_timing);
}

void VI_RegRead_V_SYNC(void)
{
	Log(TYPE_READ, "VI_V_SYNC_REG", vireg.vsync);
}

void VI_RegRead_H_SYNC(void)
{
	Log(TYPE_READ, "VI_H_SYNC_REG", vireg.hsync);
}

void VI_RegRead_LEAP(void)
{
	Log(TYPE_READ, "VI_LEAP_REG / VI_H_SYNC_LEAP_REG", vireg.hsync_leap);
}

void VI_RegRead_H_START(void)
{
	Log(TYPE_READ, "VI_H_START_REG / VI_H_VIDEO_REG", vireg.horiz_vid);
}

void VI_RegRead_V_START(void)
{
	Log(TYPE_READ, "VI_V_START_REG / VI_V_VIDEO_REG", vireg.vert_vid);
}

void VI_RegRead_V_BURST(void)
{
	Log(TYPE_READ, "VI_V_BURST_REG", vireg.vert_burst);
}

void VI_RegRead_X_SCALE(void)
{
	Log(TYPE_READ, "VI_X_SCALE_REG", vireg.x_scale);
}

void VI_RegRead_Y_SCALE(void)
{
	Log(TYPE_READ, "VI_Y_SCALE_REG", vireg.y_scale);
}

void (*VI_RegReadTable[14])(void) = {
	VI_RegRead_STATUS,VI_RegRead_ORIGIN,VI_RegRead_WIDTH,VI_RegRead_INTR,
	VI_RegRead_CURRENT,VI_RegRead_BURST,VI_RegRead_V_SYNC,VI_RegRead_H_SYNC,
	VI_RegRead_LEAP,VI_RegRead_H_START,VI_RegRead_V_START,VI_RegRead_V_BURST,
	VI_RegRead_X_SCALE,VI_RegRead_Y_SCALE
};

void VI_Reg_Read(void)
{
	VI_RegReadTable[MMIO_offs>>2]();
}

//
// -------------------- AI Read Reg --------------------
//

void AI_RegRead_DRAM_ADDR(void)		// R0: DRAM address
{
	Log(TYPE_READ, "AI_DRAM_ADDR_REG", aireg.dram_addr);
	Write32(AI_BASE_REG + AI_DRAM_ADDR_REG, aireg.dram_addr);
}

void AI_RegRead_LEN(void)			// R1: Length
{
	Log(TYPE_READ, "AI_LEN_REG", aireg.len);
	Write32(AI_BASE_REG + AI_LEN_REG, aireg.len);
}

void AI_RegRead_CONTROL(void)		// R2: DMA Control
{
	Log(TYPE_READ, "AI_CONTROL_REG", aireg.control);
	Write32(AI_BASE_REG + AI_CONTROL_REG, aireg.control);
}

void AI_RegRead_STATUS(void)		// R3: Status
{
	Log(TYPE_READ, "AI_STATUS_REG", aireg.status);
	Write32(AI_BASE_REG + AI_STATUS_REG, aireg.status);
}

void AI_RegRead_DACRATE(void)		//R4: DAC rate 14-lsb
{
	Log(TYPE_READ, "AI_DACRATE_REG", aireg.dacrate);	
	Write32(AI_BASE_REG + AI_DACRATE_REG, aireg.dacrate);
}

void AI_RegRead_BITRATE(void)		// R5: Bit rate 4-lsb
{
	Log(TYPE_READ, "AI_BITRATE_REG", aireg.bitrate);
	Write32(AI_BASE_REG + AI_BITRATE_REG, aireg.bitrate);
}

void (*AI_RegReadTable[6])(void) = {
	AI_RegRead_DRAM_ADDR, AI_RegRead_LEN, AI_RegRead_CONTROL,
	AI_RegRead_STATUS, AI_RegRead_DACRATE, AI_RegRead_BITRATE
};

void AI_Reg_Read(void)
{
	AI_RegReadTable[MMIO_offs>>2]();
}

//
// -------------------- PI Read Reg --------------------
//

void PI_RegRead_DRAM_ADDR(void)		// DRAM address
{
	Log(TYPE_READ, "PI_DRAM_ADDR_REG", pireg.dram_addr);
	Write32(PI_BASE_REG + PI_DRAM_ADDR_REG, pireg.dram_addr);
}

void PI_RegRead_CART_ADDR(void)
{
	Log(TYPE_READ, "PI_CART_ADDR_REG", pireg.cart_addr);
	Write32(PI_BASE_REG + PI_CART_ADDR_REG, pireg.cart_addr);
}

void PI_RegRead_RD_LEN(void)
{
	Log(TYPE_READ, "PI_RD_LEN_REG", pireg.rd_len);
	Write32(PI_BASE_REG + PI_RD_LEN_REG, pireg.rd_len);
}

void PI_RegRead_WR_LEN(void)
{
	Log(TYPE_READ, "PI_WR_LEN_REG", pireg.wr_len);
	Write32(PI_BASE_REG + PI_WR_LEN_REG, pireg.wr_len);
}

void PI_RegRead_STATUS(void)
{
	Log(TYPE_READ, "PI_STATUS_REG", pireg.status);
	Write32(PI_BASE_REG + PI_STATUS_REG, pireg.status);
}

void PI_RegRead_BSD_DOM1_LAT(void)	// Domain 1 latency
{
	Log(TYPE_READ, "PI_BSD_DOM1_LAT_REG", pireg.dom1_lat);
}

void PI_RegRead_BSD_DOM1_PWD(void)	// Domain 1 pulse width
{
	Log(TYPE_READ, "PI_BSD_DOM1_PWD_REG", pireg.dom1_pulse_width);
}

void PI_RegRead_BSD_DOM1_PGS(void)	// Domain 1 page size
{
	Log(TYPE_READ, "PI_BSD_DOM1_PGS_REG", pireg.dom1_page_size);
}

void PI_RegRead_BSD_DOM1_RLS(void)	// Domain 1 release duration
{
	Log(TYPE_READ, "PI_BSD_DOM1_RLS_REG", pireg.dom1_release);
}

void PI_RegRead_BSD_DOM2_LAT(void)	// Domain 2 latency
{
	Log(TYPE_READ, "PI_BSD_DOM2_LAT_REG", pireg.dom2_lat);
}

void PI_RegRead_BSD_DOM2_PWD(void)	// Domain 2 pulse width
{
	Log(TYPE_READ, "PI_BSD_DOM2_PWD_REG", pireg.dom2_pulse_width);
}

void PI_RegRead_BSD_DOM2_PGS(void)	// Domain 2 page size
{
	Log(TYPE_READ, "PI_BSD_DOM2_PGS_REG", pireg.dom2_page_size);
}

void PI_RegRead_BSD_DOM2_RLS(void)	// Domain 2 release duration
{
	Log(TYPE_READ, "PI_BSD_DOM2_RLS_REG", pireg.dom2_release);
}

void (*PI_RegReadTable[13])(void) = {
	PI_RegRead_DRAM_ADDR,PI_RegRead_CART_ADDR,PI_RegRead_RD_LEN,
	PI_RegRead_WR_LEN,PI_RegRead_STATUS,PI_RegRead_BSD_DOM1_LAT,
	PI_RegRead_BSD_DOM1_PWD,PI_RegRead_BSD_DOM1_PGS,PI_RegRead_BSD_DOM1_RLS,
	PI_RegRead_BSD_DOM2_LAT,PI_RegRead_BSD_DOM2_PWD,PI_RegRead_BSD_DOM2_PGS,
	PI_RegRead_BSD_DOM2_RLS
};

void PI_Reg_Read(void)
{
	PI_RegReadTable[MMIO_offs>>2]();
}

//
// -------------------- RI Read Reg --------------------
//

void RI_RegRead_MODE(void)
{
	Log(TYPE_READ, "RI_MODE_REG", rireg.mode);
}
void RI_RegRead_CONFIG(void)
{
	Log(TYPE_READ, "RI_CONFIG_REG", rireg.config);
}
void RI_RegRead_CURRENT_LOAD(void)
{
	Log(TYPE_READ, "RI_CURRENT_LOAD_REG", rireg.current_load);
}
void RI_RegRead_SELECT(void)
{
	Log(TYPE_READ, "RI_SELECT_REG", rireg.select);
}
void RI_RegRead_REFRESH(void)
{
	Log(TYPE_READ, "RI_REFRESH_REG / RI_COUNT_REG", rireg.refresh);
}
void RI_RegRead_LATENCY(void)
{
	Log(TYPE_READ, "RI_LATENCY_REG", rireg.latency);
}
void RI_RegRead_RERROR(void)
{
	Log(TYPE_READ, "RI_RERROR_REG", rireg.rerror);
}
void RI_RegRead_WERROR(void)
{
	Log(TYPE_READ, "RI_WERROR_REG", rireg.werror);
}

void (*RI_RegReadTable[8])(void) = {
	RI_RegRead_MODE,RI_RegRead_CONFIG,RI_RegRead_CURRENT_LOAD,
	RI_RegRead_SELECT,RI_RegRead_REFRESH,RI_RegRead_LATENCY,
	RI_RegRead_RERROR,RI_RegRead_WERROR
};

void RI_Reg_Read(void)
{
	RI_RegReadTable[MMIO_offs>>2]();
}

//
// -------------------- SI Read Reg --------------------
//

void SI_Reg_Read(void)
{
    switch (MMIO_offs)
    {
        case SI_DRAM_ADDR_REG:                  // R0: DRAM address
            Log(TYPE_READ, "SI_DRAM_ADDR_REG", sireg.dram_addr);
			Write32(SI_BASE_REG + SI_DRAM_ADDR_REG, sireg.dram_addr);
            break;

        case SI_PIF_ADDR_RD64B_REG:             // R1: 64B PIF->DRAM
            Log(TYPE_READ, "SI_PIF_ADDR_RD64B_REG", sireg.pif_addr_rd64b);
			Write32(SI_BASE_REG + SI_PIF_ADDR_RD64B_REG, sireg.pif_addr_rd64b);
            break;

        case SI_PIF_ADDR_WR64B_REG:             // R4: 64B DRAM->PIF
            Log(TYPE_READ, "SI_PIF_ADDR_WR64B_REG", sireg.pif_addr_wr64b);
			Write32(SI_BASE_REG + SI_PIF_ADDR_WR64B_REG, sireg.pif_addr_wr64b);
            break;

        case SI_STATUS_REG:                     // R6: Status
            Log(TYPE_READ, "SI_STATUS_REG", sireg.status);
			Write32(SI_BASE_REG + SI_STATUS_REG, sireg.status);
            break;
    }
}

//
// -------------------- PIF ROM Read Reg --------------------
//

void PIF_ROM_Reg_Read(void)
{
}

//
// -------------------- PIF RAM Read Reg --------------------
//

void PIF_RAM_Reg_Read(void)
{
}

//
// -------------------- LEO Read Reg --------------------
//

void LEO_Reg_Read(void)
{
    switch (MMIO_offs)
    {
        case LEO_NOTEXIST_REG:
            Log(TYPE_READ, "LEO(DD64)_NOT_EXIST_REG", leoreg.not_exist);
            break;
    }
}

// *******************************************************************************************************
// *******************************************************************************************************
//
//                                            WRITE MMIO CALLS
//
// *******************************************************************************************************
// *******************************************************************************************************

//
// -------------------- RDRAM Write Reg --------------------
//

void RDRAM_RegWrite_CONFIG(void)
{
	Log(TYPE_WRITE, "RDRAM_CONFIG_REG / RDRAM_DEVICE_TYPE_REG", MMIO_val[0]);
}

void RDRAM_RegWrite_DEVICE_ID(void)
{
	Log(TYPE_WRITE, "RDRAM_DEVICE_ID_REG", MMIO_val[0]);
}

void RDRAM_RegWrite_DELAY(void)
{
	Log(TYPE_WRITE, "RDRAM_DELAY_REG", MMIO_val[0]);
}

void RDRAM_RegWrite_MODE(void)
{
	Log(TYPE_WRITE, "RDRAM_MODE_REG", MMIO_val[0]);
}

void RDRAM_RegWrite_REF_INTERVAL(void)
{
	Log(TYPE_WRITE, "RDRAM_REF_INTERVAL_REG", MMIO_val[0]);
}

void RDRAM_RegWrite_REF_ROW(void)
{
	Log(TYPE_WRITE, "RDRAM_REF_ROW_REG", MMIO_val[0]);
}

void RDRAM_RegWrite_RAS_INTERVAL(void)
{
	Log(TYPE_WRITE, "RDRAM_RAS_INTERVAL_REG", MMIO_val[0]);
}

void RDRAM_RegWrite_MIN_INTERVAL(void)
{
	Log(TYPE_WRITE, "RDRAM_MIN_INTERVAL_REG", MMIO_val[0]);
}

void RDRAM_RegWrite_ADDR_SELECT(void)
{
	Log(TYPE_WRITE, "RDRAM_ADDR_SELECT_REG", MMIO_val[0]);
}

void RDRAM_RegWrite_DEVICE_MANUF(void)
{
	Log(TYPE_WRITE, "RDRAM_DEVICE_MANUF_REG", MMIO_val[0]);
}

void (*RDRAM_RegWriteTable[10])(void) = {
    RDRAM_RegWrite_CONFIG, RDRAM_RegWrite_DEVICE_ID, RDRAM_RegWrite_DELAY,
	RDRAM_RegWrite_MODE, RDRAM_RegWrite_REF_INTERVAL,RDRAM_RegWrite_REF_ROW,
	RDRAM_RegWrite_RAS_INTERVAL,RDRAM_RegWrite_MIN_INTERVAL,
	RDRAM_RegWrite_ADDR_SELECT,RDRAM_RegWrite_DEVICE_MANUF
};

void RDRAM_Reg_Write(void)
{
	RDRAM_RegWriteTable[MMIO_offs>>2]();
}

//
// -------------------- SP Write Reg --------------------
//

void CPU_RSP_SP_RegWrite_MEM_ADDR(ulong Val)
{
    Val &= 0x00001fff;         //only 13 bits..
    Val |= 0x04000000;         //must be in SP_DMEM or SP_IMEM
    spreg.mem_addr = Val;
}

void SP_RegWrite_MEM_ADDR(void)
{
    Log(TYPE_WRITE, "SP_MEM_ADDR_REG", MMIO_val[0]);
    CPU_RSP_SP_RegWrite_MEM_ADDR(MMIO_val[0]);
}

void CPU_RSP_SP_RegWrite_DRAM_ADDR(ulong Val)
{
    spreg.dram_addr = Val;
}

void SP_RegWrite_DRAM_ADDR(void)
{
    Log(TYPE_WRITE, "SP_DRAM_ADDR_REG", MMIO_val[0]);
	CPU_RSP_SP_RegWrite_DRAM_ADDR(MMIO_val[0]);
}

void CPU_RSP_SP_RegWrite_RD_LEN(ulong Val)
{
    spreg.read_len = Val;
    // do the DMA from RDRAM -> (SP_IMEM or SP_DMEM)
    MemoryCopy(spreg.mem_addr, spreg.dram_addr, spreg.read_len+1);
}

void SP_RegWrite_RD_LEN(void)
{
    Log(TYPE_WRITE, "SP_RD_LEN_REG", MMIO_val[0]);
	CPU_RSP_SP_RegWrite_RD_LEN(MMIO_val[0]);
}

void CPU_RSP_SP_RegWrite_WR_LEN(ulong Val)
{
    spreg.write_len = Val;
    // do the DMA from (SP_IMEM or SP_DMEM) -> RDRAM
    MemoryCopy(spreg.dram_addr, spreg.mem_addr, spreg.write_len+1);
}

void SP_RegWrite_WR_LEN(void)
{
	Log(TYPE_WRITE, "SP_WR_LEN_REG", MMIO_val[0]);
	CPU_RSP_SP_RegWrite_WR_LEN(MMIO_val[0]);
}

void CPU_RSP_SP_RegWrite_STATUS(ulong Val)
{
	Log(TYPE_WRITE, "SP_STATUS", Val);

	if(Val & SP_CLR_HALT)
	{
		spreg.status=spreg.status & ~(SP_STATUS_HALT);
		// this might start RSP if nothing else will stop it..
	}

	if(Val & SP_SET_HALT)
	{
		spreg.status=spreg.status | SP_STATUS_HALT;
		RSP->RSP_Stop();
	}

	if(Val & SP_CLR_BROKE)
	{
		spreg.status = spreg.status & ~(SP_STATUS_BROKE);
	}

	if(Val & SP_CLR_INTR)
	{
		mireg.intr &= ~MI_INTR_SP;				// Clear SP interrupt
		Log(TYPE_WRITE, "Clearing SP Interrupt", 0);

		// If no MI interrupts left, clear the hardware interrupt.
		if (!(mireg.intr & MI_INTR_MASK))
			c0reg.Cause &= ~C0CAUSE_IP2;
	}

	if(Val & SP_SET_INTR)
	{
		mireg.intr |= MI_INTR_SP;				// Set SP interrupt
		Log(TYPE_WRITE, "Setting SP Interrupt", 0);

		// Set the hardware interrupt.
		c0reg.Cause |= C0CAUSE_IP2;
		RegenInterrupts();
	}

	if(Val & SP_CLR_SSTEP)
	{
		// is this right... or does this tell the RSP to SSTEP and then when
		// it does a SSTEP it sets/clears the spreg.status bit... 
		//(doesnt matter now..)
		spreg.status = spreg.status & ~(SP_STATUS_SSTEP);
	}
	if(Val & SP_SET_SSTEP)
	{
		// is this right... or does this tell the RSP to SSTEP and then when
		// it does a SSTEP it sets/clears the spreg.status bit... 
		//(doesnt matter now..)
		spreg.status = spreg.status | (SP_STATUS_SSTEP);
	}

	if(Val & SP_CLR_INTR_BREAK)
	{
		spreg.status = spreg.status & ~(SP_STATUS_INTR_BREAK);
	}
	if(Val & SP_SET_INTR_BREAK)
	{
		spreg.status = spreg.status | (SP_STATUS_INTR_BREAK);
	}
	if(Val & SP_CLR_SIG0)
	{
		spreg.status = spreg.status & ~(SP_STATUS_SIG0);
	}
	if(Val & SP_SET_SIG0)
	{
		spreg.status = spreg.status | (SP_STATUS_SIG0);
	}
	if(Val & SP_CLR_SIG1)
	{
		spreg.status = spreg.status & ~(SP_STATUS_SIG1);
	}
	if(Val & SP_SET_SIG1)
	{
		spreg.status = spreg.status | (SP_STATUS_SIG1);
	}
	if(Val & SP_CLR_SIG2)
	{
		spreg.status = spreg.status & ~(SP_STATUS_SIG2);
	}
	if(Val & SP_SET_SIG2)
	{
		spreg.status = spreg.status | (SP_STATUS_SIG2);
	}
	if(Val & SP_CLR_SIG3)
	{
		spreg.status = spreg.status & ~(SP_STATUS_SIG3);
	}
	if(Val & SP_SET_SIG3)
	{
		spreg.status = spreg.status | (SP_STATUS_SIG3);
	}
	if(Val & SP_CLR_SIG4)
	{
		spreg.status = spreg.status & ~(SP_STATUS_SIG0);
	}
	if(Val & SP_SET_SIG4)
	{
		spreg.status = spreg.status | (SP_STATUS_SIG4);
	}
	if(Val & SP_CLR_SIG5)
	{
		spreg.status = spreg.status & ~(SP_STATUS_SIG1);
	}
	if(Val & SP_SET_SIG5)
	{
		spreg.status = spreg.status | (SP_STATUS_SIG5);
	}
	if(Val & SP_CLR_SIG6)
	{
		spreg.status = spreg.status & ~(SP_STATUS_SIG2);
	}
	if(Val & SP_SET_SIG7)
	{
		spreg.status = spreg.status | (SP_STATUS_SIG7);
	}
	if(Val & SP_CLR_SIG7)
	{
		spreg.status = spreg.status & ~(SP_STATUS_SIG3);
	}

	// Check to see if the RSP should run... OK to blindly call again, RSP_Start() is ignored if already running.
    if ((!(spreg.status & SP_STATUS_BROKE)) && (!(spreg.status & SP_STATUS_HALT)))
        RSP->RSP_Start();
}

void SP_RegWrite_STATUS(void)
{
    Log(TYPE_WRITE, "SP_STATUS_REG", MMIO_val[0]);
	CPU_RSP_SP_RegWrite_STATUS(MMIO_val[0]);
}

void CPU_RSP_SP_RegWrite_DMA_FULL(ulong Val)
{
	// read only register (invalid to write)
}

void SP_RegWrite_DMA_FULL(void)
{
	Log(TYPE_WRITE, "SP_DMA_FULL_REG", MMIO_val[0]);
	CPU_RSP_SP_RegWrite_STATUS(MMIO_val[0]);
}

void CPU_RSP_SP_RegWrite_DMA_BUSY(ulong Val)
{
	// read only register (invalid to write)
}

void SP_RegWrite_DMA_BUSY(void)
{
	Log(TYPE_WRITE, "SP_DMA_BUSY_REG", MMIO_val[0]);
	CPU_RSP_SP_RegWrite_DMA_BUSY(MMIO_val[0]);
}

void CPU_RSP_SP_RegWrite_SEMAPHORE(ulong Val)
{
   spreg.semaphore = 0; // clear semaphore on write
}

void SP_RegWrite_SEMAPHORE(void)
{
	Log(TYPE_WRITE, "SP_SEMAPHORE_REG", MMIO_val[0]);
	CPU_RSP_SP_RegWrite_SEMAPHORE(MMIO_val[0]);
}


void (*SP_RegWriteTable[8])(void) = {
	SP_RegWrite_MEM_ADDR, SP_RegWrite_DRAM_ADDR, SP_RegWrite_RD_LEN,
	SP_RegWrite_WR_LEN, SP_RegWrite_STATUS, SP_RegWrite_DMA_FULL,
	SP_RegWrite_DMA_BUSY, SP_RegWrite_SEMAPHORE
};

void SP_Reg_Write(void)
{
	SP_RegWriteTable[MMIO_offs>>2]();
}

//
// -------------------- SP PC Write Reg --------------------
//

void SP_PC_Write(void)
{
    Log(TYPE_WRITE, "SP_PC_REG", MMIO_val[0]);
	sp_pc.PC = MMIO_val[0];
}

//
// -------------------- SP IBIST Write Reg --------------------
//

void SP_IBIST_Write(void)
{
    Log(TYPE_WRITE, "SP_IBIST_REG", MMIO_val[0]);
	sp_ibist.ibist = MMIO_val[0];
}

//
// -------------------- DPC Write Reg --------------------
//

void CPU_RSP_DPC_RegWrite_START(ulong Val)
{
	dpcreg.start = SP_DMEM_BASE + (Val & 0x0FFF);
}

void DPC_RegWrite_START(void)
{
	Log(TYPE_WRITE, "DPC_START_REG", MMIO_val[0]);
	CPU_RSP_DPC_RegWrite_START(MMIO_val[0]);
}

void CPU_RSP_DPC_RegWrite_END(ulong Val)
{
	dpcreg.end = SP_DMEM_BASE + (Val & 0x0FFF);
	DoRDPEmulation();
}

void DPC_RegWrite_END(void)
{
	Log(TYPE_WRITE, "DPC_END_REG", MMIO_val[0]);
	CPU_RSP_DPC_RegWrite_END(MMIO_val[0]);
}

void CPU_RSP_DPC_RegWrite_CURRENT(ulong Val)
{
	// Read only...
}

void DPC_RegWrite_CURRENT(void)
{
	Log(TYPE_WRITE, "DPC_CURRENT_REG", MMIO_val[0]);
	CPU_RSP_DPC_RegWrite_CURRENT(MMIO_val[0]);
}

void CPU_RSP_DPC_RegWrite_STATUS(ulong Val)
{
	if (Val & DPC_CLR_XBUS_DMEM_DMA)
	{
		dpcreg.status &= ~DPC_STATUS_XBUS_DMEM_DMA;		// Clear XBUS DMEM DMA
		Log(TYPE_WRITE, "Clearing xbus dmem dma", 0);
	}

	if (Val & DPC_SET_XBUS_DMEM_DMA)
	{
		dpcreg.status |= DPC_STATUS_XBUS_DMEM_DMA;		// Set XBUS DMEM DMA
		Log(TYPE_WRITE, "Setting xbus dmem dma", 0);
	}

	if (Val & DPC_CLR_FREEZE)
	{
		dpcreg.status &= ~DPC_STATUS_FREEZE;			// Clear freeze
		Log(TYPE_WRITE, "Clearing freeze", 0);
	}

	if (Val & DPC_SET_FREEZE)
	{
		dpcreg.status |= DPC_STATUS_FREEZE;				// Set freeze
		Log(TYPE_WRITE, "Setting freeze", 0);
	}

	if (Val & DPC_CLR_FLUSH)
	{
		dpcreg.status &= ~DPC_STATUS_FLUSH;				// Clear flush
		Log(TYPE_WRITE, "Clearing flush", 0);
	}

	if (Val & DPC_SET_FLUSH)
	{
		dpcreg.status |= DPC_STATUS_FLUSH;				// Set flush
		Log(TYPE_WRITE, "Setting flush", 0);
	}

	if (Val & DPC_CLR_TMEM_CTR)
	{
		dpcreg.tmem = 0x00000000;						// Clear TMEM counter
		Log(TYPE_WRITE, "Clearing TMEM counter", 0);
	}

	if (Val & DPC_CLR_PIPE_CTR)
	{
		dpcreg.pipebusy = 0x00000000;					// Clear pipe busy counter
		Log(TYPE_WRITE, "Clearing pipe busy counter", 0);
	}

	if (Val & DPC_CLR_CMD_CTR)
	{
		dpcreg.bufbusy = 0x00000000;					// Clear command (?) counter
		Log(TYPE_WRITE, "Clearing buf busy (command?) counter", 0);
	}

	if (Val & DPC_CLR_CLOCK_CTR)
	{
		dpcreg.clock = 0x00000000;						// Clear clock counter
		Log(TYPE_WRITE, "Clearing clock counter", 0);
	}
}

void DPC_RegWrite_STATUS(void)
{
	Log(TYPE_WRITE, "DPC_STATUS_REG", MMIO_val[0]);
	CPU_RSP_DPC_RegWrite_STATUS(MMIO_val[0]);
}

void CPU_RSP_DPC_RegWrite_CLOCK(ulong Val)
{
	// Read only...
}

void DPC_RegWrite_CLOCK(void)
{
	Log(TYPE_WRITE, "DPC_CLOCK_REG", MMIO_val[0]);
	CPU_RSP_DPC_RegWrite_CLOCK(MMIO_val[0]);
}

void CPU_RSP_DPC_RegWrite_BUFBUSY(ulong Val)
{
	// Read only...
}

void DPC_RegWrite_BUFBUSY(void)
{
	Log(TYPE_WRITE, "DPC_BUFBUSY_REG", MMIO_val[0]);
	CPU_RSP_DPC_RegWrite_BUFBUSY(MMIO_val[0]);
}

void CPU_RSP_DPC_RegWrite_PIPEBUSY(ulong Val)
{
	// Read only...
}

void DPC_RegWrite_PIPEBUSY(void)
{
	Log(TYPE_WRITE, "DPC_PIPEBUSY_REG", MMIO_val[0]);
	CPU_RSP_DPC_RegWrite_PIPEBUSY(MMIO_val[0]);
}

void CPU_RSP_DPC_RegWrite_TMEM(ulong Val)
{
	// Read only...
}

void DPC_RegWrite_TMEM(void)
{
	Log(TYPE_WRITE, "DPC_TMEM_REG", MMIO_val[0]);
	CPU_RSP_DPC_RegWrite_TMEM(MMIO_val[0]);
}

void (*DPC_RegWriteTable[8])(void) = {
	DPC_RegWrite_START, DPC_RegWrite_END, DPC_RegWrite_CURRENT,
	DPC_RegWrite_STATUS, DPC_RegWrite_CLOCK, DPC_RegWrite_BUFBUSY,
	DPC_RegWrite_PIPEBUSY, DPC_RegWrite_TMEM
};

void DPC_Reg_Write(void)
{
	DPC_RegWriteTable[MMIO_offs>>2]();
}

//
// -------------------- DPS Write Reg --------------------
//

void DPS_Reg_Write(void)
{
    switch (MMIO_offs)
    {
        case DPS_TBIST_REG:
            Log(TYPE_WRITE, "DPS_TBIST_REG", MMIO_val[0]);
            break;

        case DPS_TEST_MODE_REG:
            Log(TYPE_WRITE, "DPS_TEST_MODE_REG", MMIO_val[0]);
            break;

        case DPS_BUFTEST_ADDR_REG:
            Log(TYPE_WRITE, "DPS_BUFTEST_ADDR_REG", MMIO_val[0]);
            break;

        case DPS_BUFTEST_DATA_REG:
            Log(TYPE_WRITE, "DPS_BUFTEST_DATA_REG", MMIO_val[0]);
            break;
    }
}

//
// -------------------- MI Write Reg --------------------
//

void MI_Reg_Write(void)
{
    switch (MMIO_offs)
    {
        case MI_INIT_MODE_REG:
            Log(TYPE_WRITE, "MI_INIT_MODE_REG", MMIO_val[0]);

			if (MMIO_val[0] & MI_CLR_INIT)
			{
				mireg.mode &= ~MI_MODE_INIT;		// Clear init mode
				Log(TYPE_WRITE, "Clearing init mode", 0);
			}

			if (MMIO_val[0] & MI_SET_INIT)		
			{
				mireg.mode |= MI_MODE_INIT;			// Set init mode
				Log(TYPE_WRITE, "Setting init mode", 0);
			}

			if (MMIO_val[0] & MI_CLR_EBUS)
			{
				mireg.mode &= ~MI_MODE_EBUS;		// Clear ebus test mode
				Log(TYPE_WRITE, "Clearing ebus test mode", 0);				
			}

			if (MMIO_val[0] & MI_SET_EBUS)
			{
				mireg.mode |= MI_MODE_EBUS;			// Set ebus test mode
				Log(TYPE_WRITE, "Setting ebus test mode", 0);
			}

			if (MMIO_val[0] & MI_CLR_DP_INTR)
			{
				mireg.intr &= ~MI_INTR_DP;			// Clear DP interrupt
				Log(TYPE_WRITE, "Clearing DP interrupt", 0);

				// If no MI interrupts left, clear the hardware interrupt.
				if (!(mireg.intr & MI_INTR_MASK))
					c0reg.Cause &= ~C0CAUSE_IP2;
			}

			if (MMIO_val[0] & MI_CLR_RDRAM)
			{
				mireg.mode &= MI_MODE_RDRAM;		// Clear RDRAM reg mode
				Log(TYPE_WRITE, "Clearing RDRAM reg mode", 0);
			}

			if (MMIO_val[0] & MI_SET_RDRAM)
			{
				mireg.mode |= MI_MODE_RDRAM;		// Set RDRAM reg mode
				Log(TYPE_WRITE, "Setting RDRAM reg mode", 0);
			}
            break;

        case MI_VERSION_REG:
            Log(TYPE_WRITE, "MI_VERSION_REG", MMIO_val[0]);
            break;

        case MI_INTR_REG:
            Log(TYPE_WRITE, "MI_INTR_REG", MMIO_val[0]);
            break;

        case MI_INTR_MASK_REG:
            Log(TYPE_WRITE, "MI_INTR_MASK_REG", MMIO_val[0]);

			if (MMIO_val[0] & MI_INTR_MASK_CLR_SP)
			{
				mireg.intr_mask &= ~MI_INTR_MASK_SP;	// Clear SP mask
				Log(TYPE_WRITE, "Clearing SP interrupt mask", 0);
			}

			if (MMIO_val[0] & MI_INTR_MASK_SET_SP)
			{
				mireg.intr_mask |= MI_INTR_MASK_SP;		// Set SP mask
				Log(TYPE_WRITE, "Setting SP interrupt mask", 0);

				if (mireg.intr & MI_INTR_SP)
				{
					Log(TYPE_WRITE, "*** Signal Processor Interrupt", 0);
					c0reg.Cause |= C0CAUSE_IP2;
					RegenInterrupts();
				}
			}

			if (MMIO_val[0] & MI_INTR_MASK_CLR_SI)
			{
				mireg.intr_mask &= ~MI_INTR_MASK_SI;	// Clear SI mask
				Log(TYPE_WRITE, "Clearing SI interrupt mask", 0);
			}

			if (MMIO_val[0] & MI_INTR_MASK_SET_SI)
			{
				mireg.intr_mask |= MI_INTR_MASK_SI;		// Set SI mask
				Log(TYPE_WRITE, "Setting SI interrupt mask", 0);

				if (mireg.intr & MI_INTR_SI)
				{
					Log(TYPE_WRITE, "*** Serial Interface Interrupt", 0);
					c0reg.Cause |= C0CAUSE_IP2;
					RegenInterrupts();
				}
			}

			if (MMIO_val[0] & MI_INTR_MASK_CLR_AI)
			{
				mireg.intr_mask &= ~MI_INTR_MASK_AI;	// Clear AI mask
				Log(TYPE_WRITE, "Clearing AI interrupt mask", 0);
			}

			if (MMIO_val[0] & MI_INTR_MASK_SET_AI)
			{
				mireg.intr_mask |= MI_INTR_MASK_AI;		// Set AI mask
				Log(TYPE_WRITE, "Setting AI interrupt mask", 0);

				if (mireg.intr & MI_INTR_AI)
				{
					Log(TYPE_WRITE, "*** Audio Interface Interrupt", 0);
					c0reg.Cause |= C0CAUSE_IP2;
					RegenInterrupts();
				}
			}

			if (MMIO_val[0] & MI_INTR_MASK_CLR_VI)
			{
				mireg.intr_mask &= ~MI_INTR_MASK_VI;	// Clear VI mask
				Log(TYPE_WRITE, "Clearing VI interrupt mask", 0);
			}

			if (MMIO_val[0] & MI_INTR_MASK_SET_VI)
			{
				mireg.intr_mask |= MI_INTR_MASK_VI;		// Set VI mask
				Log(TYPE_WRITE, "Setting VI interrupt mask", 0);

				if (mireg.intr & MI_INTR_VI)
				{
					Log(TYPE_WRITE, "*** Vertical Retrace Interrupt", 0);
					c0reg.Cause |= C0CAUSE_IP2;
					RegenInterrupts();
				}
			}

			if (MMIO_val[0] & MI_INTR_MASK_CLR_PI)
			{
				mireg.intr_mask &= ~MI_INTR_MASK_PI;	// Clear PI mask
				Log(TYPE_WRITE, "Clearing PI interrupt mask", 0);
			}

			if (MMIO_val[0] & MI_INTR_MASK_SET_PI)
			{
				mireg.intr_mask |= MI_INTR_MASK_PI;		// Set PI mask
				Log(TYPE_WRITE, "Setting PI interrupt mask", 0);

				if (mireg.intr & MI_INTR_PI)
				{
					Log(TYPE_WRITE, "*** Peripherial Interface Interrupt", 0);
					c0reg.Cause |= C0CAUSE_IP2;
					RegenInterrupts();
				}
			}

			if (MMIO_val[0] & MI_INTR_MASK_CLR_DP)
			{
				mireg.intr_mask &= ~MI_INTR_MASK_DP;	// Clear DP mask
				Log(TYPE_WRITE, "Clearing DP interrupt mask", 0);
			}

			if (MMIO_val[0] & MI_INTR_MASK_SET_DP)
			{
				mireg.intr_mask |= MI_INTR_MASK_DP;		// Set DP mask
				Log(TYPE_WRITE, "Setting DP interrupt mask", 0);

				if (mireg.intr & MI_INTR_DP)
				{
					Log(TYPE_WRITE, "*** Display Processor Interrupt", 0);
					c0reg.Cause |= C0CAUSE_IP2;
					RegenInterrupts();
				}
			}
            break;
    }
}


//
// -------------------- VI Write Reg --------------------
//

void VI_RegWrite_STATUS(void)
{
	ulong stat;
	vireg.stat_ctrl = MMIO_val[0];
    Log(TYPE_WRITE, "VI_STATUS_REG / VI_CONTROL_REG", MMIO_val[0]);
	Write32(VI_BASE_REG + VI_STATUS_REG, vireg.stat_ctrl);

    stat = vireg.stat_ctrl & 0x03;
    switch (stat)
    {
        case 0: 
            // No data (blank)
			CFB_Blank = TRUE;
            break;

        case 1: 
            // Reserved
            break;

		case 2:
			CFB_Blank = FALSE;
            CFB_BitDepth_Change(16);
            break;

        case 3:
			CFB_Blank = FALSE;
			CFB_BitDepth_Change(32);
		    break;
	}
}

void VI_RegWrite_ORIGIN(void)
{
	vireg.origin = MMIO_val[0];
	Write32(VI_BASE_REG + VI_ORIGIN_REG, vireg.origin);

    Log(TYPE_WRITE, "VI_ORIGIN_REG / VI_DRAM_ADDR_REG", MMIO_val[0]);
	CFB_Origin_Change(vireg.origin);
}

void VI_RegWrite_WIDTH(void)
{
	vireg.width = MMIO_val[0];
	Write32(VI_BASE_REG + VI_WIDTH_REG, vireg.width);

    Log(TYPE_WRITE, "VI_WIDTH_REG / VI_H_WIDTH_REG", MMIO_val[0]);
    CFB_Resolution_Change(vireg.width);
}

void VI_RegWrite_INTR(void)
{
	Log(TYPE_WRITE, "VI_INTR_REG / VI_V_INTR_REG", MMIO_val[0]);
}

void VI_RegWrite_CURRENT(void)
{
    Log(TYPE_WRITE, "VI_CURRENT_REG / VI_V_CURRENT_LINE_REG", MMIO_val[0]);

	mireg.intr &= ~MI_INTR_VI;				// Clear VI interrupt (BWG)
	Log(TYPE_WRITE, "Clearing VI Interrupt", 0);

	// If no MI interrupts left, clear the hardware interrupt.
	if (!(mireg.intr & MI_INTR_MASK))
		c0reg.Cause &= ~C0CAUSE_IP2;
}

void VI_RegWrite_BURST(void)
{
	Log(TYPE_WRITE, "VI_BURST_REG / VI_TIMING_REG", MMIO_val[0]);
}

void VI_RegWrite_V_SYNC(void)
{
	Log(TYPE_WRITE, "VI_V_SYNC_REG", MMIO_val[0]);
}

void VI_RegWrite_H_SYNC(void)
{
	Log(TYPE_WRITE, "VI_H_SYNC_REG", MMIO_val[0]);
}

void VI_RegWrite_LEAP(void)
{
	Log(TYPE_WRITE, "VI_LEAP_REG / VI_H_SYNC_LEAP_REG", MMIO_val[0]);
}

void VI_RegWrite_H_START(void)
{
	Log(TYPE_WRITE, "VI_H_START_REG / VI_H_VIDEO_REG", MMIO_val[0]);
}

void VI_RegWrite_V_START(void)
{
	Log(TYPE_WRITE, "VI_V_START_REG / VI_V_VIDEO_REG", MMIO_val[0]);
}

void VI_RegWrite_V_BURST(void)
{
	Log(TYPE_WRITE, "VI_V_BURST_REG", MMIO_val[0]);
}

void VI_RegWrite_X_SCALE(void)
{
	Log(TYPE_WRITE, "VI_X_SCALE_REG", MMIO_val[0]);
}

void VI_RegWrite_Y_SCALE(void)
{
	Log(TYPE_WRITE, "VI_Y_SCALE_REG", MMIO_val[0]);
}

void (*VI_RegWriteTable[14])(void) = {
	VI_RegWrite_STATUS,VI_RegWrite_ORIGIN,VI_RegWrite_WIDTH,VI_RegWrite_INTR,
	VI_RegWrite_CURRENT,VI_RegWrite_BURST,VI_RegWrite_V_SYNC,VI_RegWrite_H_SYNC,
	VI_RegWrite_LEAP,VI_RegWrite_H_START,VI_RegWrite_V_START,VI_RegWrite_V_BURST,
	VI_RegWrite_X_SCALE,VI_RegWrite_Y_SCALE
};


void VI_Reg_Write(void)
{
	VI_RegWriteTable[MMIO_offs>>2]();
}

//
// -------------------- AI Write Reg --------------------
//

void AI_RegWrite_DRAM_ADDR(void)
{
	Log(TYPE_WRITE, "AI_DRAM_ADDR_REG", MMIO_val[0]);

	switch (aireg_dram_state)
	{
		case 0:						// FIFO empty
			aireg_dram_state++;
			aireg.dram_addr = MMIO_val[0];
			Write32(AI_BASE_REG + AI_DRAM_ADDR_REG, aireg.dram_addr);
			break;

		case 1:						// In use
			aireg_dram_state++;
			aireg_dram_hold = MMIO_val[0];

			// Check to see if the FIFO is filled.
			if (aireg_len_state == 2) aireg.status |= AI_STATUS_FIFO_FULL;
			break;

		case 2:						// FIFO full
			break;
	}
}

void AI_RegWrite_LEN(void)
{
	Log(TYPE_WRITE, "AI_LEN_REG", MMIO_val[0]);

	switch (aireg_len_state)
	{
		case 0:						// FIFO empty
			aireg_len_state++;
			aireg.len = MMIO_val[0];
			Write32(AI_BASE_REG + AI_LEN_REG, aireg.len);

			// Play buffer!
			Audio_Play((sshort *)FindMemoryPtr(aireg.dram_addr), aireg.len);
			break;

		case 1:						// In use
			aireg_len_state++;
			aireg_len_hold = MMIO_val[0];

			// Check to see if the FIFO is filled.
			if (aireg_dram_state == 2) aireg.status |= AI_STATUS_FIFO_FULL;
			break;

		case 2:						// FIFO full
			break;
	}
}

void AI_RegWrite_CONTROL(void)
{
	Log(TYPE_WRITE, "AI_CONTROL_REG", MMIO_val[0]);

	aireg.control = MMIO_val[0];
	Write32(AI_BASE_REG + AI_CONTROL_REG, aireg.control);

	if (aireg.control)
		Audio_Unpause();
	else
		Audio_Pause();
}

void AI_RegWrite_STATUS(void)
{
	Log(TYPE_WRITE, "AI_STATUS_REG", MMIO_val[0]);

	mireg.intr &= ~MI_INTR_AI;				// Clear AI interrupt
	Log(TYPE_WRITE, "Clearing AI Interrupt", 0);

	// If no MI interrupts left, clear the hardware interrupt.
	if (!(mireg.intr & MI_INTR_MASK))
		c0reg.Cause &= ~C0CAUSE_IP2;

	Write32(AI_BASE_REG + AI_STATUS_REG, aireg.status);
}

void AI_RegWrite_DACRATE(void)
{
	char buf[128];
	ulong rate;
	Log(TYPE_WRITE, "AI_DACRATE_REG", MMIO_val[0]);

	aireg.dacrate = MMIO_val[0];
	Write32(AI_BASE_REG + AI_DACRATE_REG, aireg.dacrate);

	// AAARGH!  A freakin' magic number!  This value gets loaded into T0 when osAiSetFrequency() is called.  It's loaded
	// by the first two instructions, LUI and LW.
	rate = (0x02E6D354 / aireg.dacrate) + 1;
	Audio_SampleRate(rate);

	sprintf(buf, "### Setting playback rate to %d", rate);
	Log(TYPE_WRITE, buf, 0);
}

void AI_RegWrite_BITRATE(void)
{
	char buf[128];
	Log(TYPE_WRITE, "AI_BITRATE_REG", MMIO_val[0]);

	aireg.bitrate = MMIO_val[0];
	Write32(AI_BASE_REG + AI_BITRATE_REG, aireg.bitrate);

	Audio_BitRate(aireg.bitrate + 1);

	sprintf(buf, "### Setting bit depth to %d", aireg.bitrate + 1);
	Log(TYPE_WRITE, buf, 0);
}


void (*AI_RegWriteTable[6])(void) = {
	AI_RegWrite_DRAM_ADDR, AI_RegWrite_LEN, AI_RegWrite_CONTROL,
	AI_RegWrite_STATUS, AI_RegWrite_DACRATE, AI_RegWrite_BITRATE
};

void AI_Reg_Write(void)
{
	AI_RegWriteTable[MMIO_offs>>2]();
}

//
// -------------------- PI Write Reg --------------------
//

void PI_RegWrite_DRAM_ADDR(void)
{
	Log(TYPE_WRITE, "PI_DRAM_ADDR_REG", MMIO_val[0]);
    pireg.dram_addr = MMIO_val[0];
	Write32(PI_BASE_REG + PI_DRAM_ADDR_REG, pireg.dram_addr);
    DMA_dest_addr = MMIO_val[0];
}

void PI_RegWrite_CART_ADDR(void)
{
	Log(TYPE_WRITE, "PI_CART_ADDR_REG", MMIO_val[0]);
    pireg.cart_addr = MMIO_val[0];
	Write32(PI_BASE_REG + PI_CART_ADDR_REG, pireg.cart_addr);
    DMA_src_addr = MMIO_val[0];
}

void PI_RegWrite_RD_LEN(void)
{
	Log(TYPE_WRITE, "PI_RD_LEN_REG", MMIO_val[0]);
    pireg.rd_len = MMIO_val[0];
	Write32(PI_BASE_REG + PI_RD_LEN_REG, pireg.rd_len);
}

void PI_RegWrite_WR_LEN(void)
{
	Log(TYPE_WRITE, "PI_WR_LEN_REG", MMIO_val[0]);
    pireg.wr_len = MMIO_val[0];
	Write32(PI_BASE_REG + PI_WR_LEN_REG, pireg.wr_len);
    DMA_bytes_to_copy = MMIO_val[0] + 1;

    // Start a DMA transfer...
    DMA_running   = TRUE;
    pireg.status |= PI_STATUS_DMA_BUSY;
	Write32(PI_BASE_REG + PI_STATUS_REG, pireg.status);
}

void PI_RegWrite_STATUS(void)
{
	Log(TYPE_WRITE, "PI_STATUS_REG", MMIO_val[0]);

	if (MMIO_val[0] & PI_STATUS_RESET)
	{
		Log(TYPE_WRITE, "Resetting PIC", 0);

		// We want to do a PIC reset.  If we're busy, set off an interrupt.
		if (pireg.status & PI_STATUS_DMA_BUSY)
		{
			Log(TYPE_WRITE, "PIC was BUSY!  Generating PI interrupt", 0);
			EventHeapInsert(c0reg.Count, PeripherialInterfaceInterrupt);
		}
		pireg.status = 0x00000000;				// Reset PIC
	}

	if (MMIO_val[0] & PI_STATUS_CLR_INTR)
	{
		mireg.intr &= ~MI_INTR_PI;				// Clear PI interrupt
		Log(TYPE_WRITE, "Clearing PI Interrupt", 0);

		// If no MI interrupts left, clear the hardware interrupt.
		if (!(mireg.intr & MI_INTR_MASK))
			c0reg.Cause &= ~C0CAUSE_IP2;
	}

	Write32(PI_BASE_REG + PI_STATUS_REG, pireg.status);
}

void PI_RegWrite_BSD_DOM1_LAT(void)
{
	Log(TYPE_WRITE, "PI_BSD_DOM1_LAT_REG", MMIO_val[0]);
}

void PI_RegWrite_BSD_DOM1_PWD(void)
{
	Log(TYPE_WRITE, "PI_BSD_DOM1_PWD_REG", MMIO_val[0]);
}

void PI_RegWrite_BSD_DOM1_PGS(void)
{
	Log(TYPE_WRITE, "PI_BSD_DOM1_PGS_REG", MMIO_val[0]);
}

void PI_RegWrite_BSD_DOM1_RLS(void)
{
	Log(TYPE_WRITE, "PI_BSD_DOM1_RLS_REG", MMIO_val[0]);
}

void PI_RegWrite_BSD_DOM2_LAT(void)
{
	Log(TYPE_WRITE, "PI_BSD_DOM2_LAT_REG", MMIO_val[0]);
}

void PI_RegWrite_BSD_DOM2_PWD(void)
{
	Log(TYPE_WRITE, "PI_BSD_DOM2_PWD_REG", MMIO_val[0]);
}

void PI_RegWrite_BSD_DOM2_PGS(void)
{
	Log(TYPE_WRITE, "PI_BSD_DOM2_PGS_REG", MMIO_val[0]);
}

void PI_RegWrite_BSD_DOM2_RLS(void)
{
	Log(TYPE_WRITE, "PI_BSD_DOM2_RLS_REG", MMIO_val[0]);
}

void (*PI_RegWriteTable[13])(void) = {
	PI_RegWrite_DRAM_ADDR,PI_RegWrite_CART_ADDR,PI_RegWrite_RD_LEN,
	PI_RegWrite_WR_LEN,PI_RegWrite_STATUS,PI_RegWrite_BSD_DOM1_LAT,
	PI_RegWrite_BSD_DOM1_PWD,PI_RegWrite_BSD_DOM1_PGS,PI_RegWrite_BSD_DOM1_RLS,
	PI_RegWrite_BSD_DOM2_LAT,PI_RegWrite_BSD_DOM2_PWD,PI_RegWrite_BSD_DOM2_PGS,
	PI_RegWrite_BSD_DOM2_RLS
};

void PI_Reg_Write(void)
{
	PI_RegWriteTable[MMIO_offs>>2]();
}

//
// -------------------- RI Write Reg --------------------
//

void RI_RegWrite_MODE(void)
{
	Log(TYPE_WRITE, "RI_MODE_REG", MMIO_val[0]);
}

void RI_RegWrite_CONFIG(void)
{
	Log(TYPE_WRITE, "RI_CONFIG_REG", MMIO_val[0]);
}

void RI_RegWrite_CURRENT_LOAD(void)
{
	Log(TYPE_WRITE, "RI_CURRENT_LOAD_REG", MMIO_val[0]);
}

void RI_RegWrite_SELECT(void)
{
	Log(TYPE_WRITE, "RI_SELECT_REG", MMIO_val[0]);
}

void RI_RegWrite_REFRESH(void)
{
	Log(TYPE_WRITE, "RI_REFRESH_REG / RI_COUNT_REG", MMIO_val[0]);
}

void RI_RegWrite_LATENCY(void)
{
	Log(TYPE_WRITE, "RI_LATENCY_REG", MMIO_val[0]);
}

void RI_RegWrite_RERROR(void)
{
	Log(TYPE_WRITE, "RI_RERROR_REG", MMIO_val[0]);
}

void RI_RegWrite_WERROR(void)
{
	Log(TYPE_WRITE, "RI_WERROR_REG", MMIO_val[0]);
}

void (*RI_RegWriteTable[8])(void) = {
	RI_RegWrite_MODE,RI_RegWrite_CONFIG,RI_RegWrite_CURRENT_LOAD,
	RI_RegWrite_SELECT,RI_RegWrite_REFRESH,RI_RegWrite_LATENCY,
	RI_RegWrite_RERROR,RI_RegWrite_WERROR
};

void RI_Reg_Write(void)
{
	RI_RegWriteTable[MMIO_offs>>2]();
}

//
// -------------------- SI Write Reg --------------------
//

void SI_Reg_Write(void)
{
    switch (MMIO_offs)
    {
        case SI_DRAM_ADDR_REG:                  // R0: DRAM address
            Log(TYPE_WRITE, "SI_DRAM_ADDR_REG", MMIO_val[0]);
			sireg.dram_addr = MMIO_val[0];
            break;

        case SI_PIF_ADDR_RD64B_REG:             // R1: 64B PIF->DRAM
            Log(TYPE_WRITE, "SI_PIF_ADDR_RD64B_REG", MMIO_val[0]);
			Log(TYPE_WRITE, "SI: Processing PIF command block", 0);

			ProcessJoypadBlock(NULL);
			MemoryCopy(sireg.dram_addr, 0x1FC007C0, 64);

			sireg.status |= SI_STATUS_INTERRUPT;

			// Add an SI interrupt to the event heap.
			EventHeapInsert(c0reg.Count, SerialInterfaceInterrupt);
            break;

        case SI_PIF_ADDR_WR64B_REG:             // R4: 64B DRAM->PIF
            Log(TYPE_WRITE, "SI_PIF_ADDR_WR64B_REG", MMIO_val[0]);
			Log(TYPE_WRITE, "SI: Initializing PIF command block", 0);

			ProcessJoypadBlock(FindMemoryPtr(sireg.dram_addr));
			sireg.status |= SI_STATUS_INTERRUPT;

			// Add an SI interrupt to the event heap.
			EventHeapInsert(c0reg.Count, SerialInterfaceInterrupt);
            break;

        case SI_STATUS_REG:                     // R6: Status
            Log(TYPE_WRITE, "SI_STATUS_REG", MMIO_val[0]);
			Log(TYPE_WRITE, "Clearing SI interrupt", 0);
			sireg.status &= ~SI_STATUS_INTERRUPT;
			mireg.intr &= ~MI_INTR_SI;				// Clear SI interrupt (BWG)

			// If no MI interrupts left, clear the hardware interrupt.
			if (!(mireg.intr & MI_INTR_MASK))
				c0reg.Cause &= ~C0CAUSE_IP2;
            break;
    }
}

//
// -------------------- PIF ROM Write Reg --------------------
//

void PIF_ROM_Reg_Write(void)
{
}

//
// -------------------- PIF RAM Write Reg --------------------
//

void PIF_RAM_Reg_Write(void)
{
}

//
// -------------------- LEO Write Reg --------------------
//

void LEO_Reg_Write(void)
{
}
