/* machine/n64.c - contains N64 hardware emulation shared between MAME and MESS */
#include "driver.h"
#include "cpu/mips/mips3.h"
#include "cpu/rsp/rsp.h"
#include "sound/custom.h"
#include "streams.h"
#include "includes/n64.h"
#include "sound/dmadac.h"
#ifdef LSB_FIRST
#define BYTE_ADDR_XOR 3
#define WORD_ADDR_XOR 1
#else
#define BYTE_ADDR_XOR 0
#define WORD_ADDR_XOR 0
#endif
UINT32 *rdram;
UINT32 *rsp_imem;
UINT32 *rsp_dmem;
//static int first_rsp = 1;
// MIPS Interface
static UINT32 mi_version;
static UINT32 mi_interrupt = 0;
static UINT32 mi_intr_mask = 0;
extern int fb_width;
void signal_rcp_interrupt(int interrupt)
{
if (mi_intr_mask & interrupt)
{
mi_interrupt |= interrupt;
cpunum_set_input_line(0, INPUT_LINE_IRQ0, ASSERT_LINE);
}
}
void clear_rcp_interrupt(int interrupt)
{
mi_interrupt &= ~interrupt;
//if (!mi_interrupt)
{
cpunum_set_input_line(0, INPUT_LINE_IRQ0, CLEAR_LINE);
}
}
READ32_HANDLER( n64_mi_reg_r )
{
switch (offset)
{
case 0x04/4: // MI_VERSION_REG
return mi_version;
case 0x08/4: // MI_INTR_REG
return mi_interrupt;
case 0x0c/4: // MI_INTR_MASK_REG
return mi_intr_mask;
default:
logerror("mi_reg_r: %08X, %08X at %08X\n", offset, mem_mask, activecpu_get_pc());
break;
}
return 0;
}
WRITE32_HANDLER( n64_mi_reg_w )
{
switch (offset)
{
case 0x00/4: // MI_INIT_MODE_REG
if (data & 0x0800)
{
clear_rcp_interrupt(DP_INTERRUPT);
}
break;
case 0x04/4: // MI_VERSION_REG
mi_version = data;
break;
case 0x0c/4: // MI_INTR_MASK_REG
{
if (data & 0x0001) mi_intr_mask &= ~0x1; // clear SP mask
if (data & 0x0002) mi_intr_mask |= 0x1; // set SP mask
if (data & 0x0004) mi_intr_mask &= ~0x2; // clear SI mask
if (data & 0x0008) mi_intr_mask |= 0x2; // set SI mask
if (data & 0x0010) mi_intr_mask &= ~0x4; // clear AI mask
if (data & 0x0020) mi_intr_mask |= 0x4; // set AI mask
if (data & 0x0040) mi_intr_mask &= ~0x8; // clear VI mask
if (data & 0x0080) mi_intr_mask |= 0x8; // set VI mask
if (data & 0x0100) mi_intr_mask &= ~0x10; // clear PI mask
if (data & 0x0200) mi_intr_mask |= 0x10; // set PI mask
if (data & 0x0400) mi_intr_mask &= ~0x20; // clear DP mask
if (data & 0x0800) mi_intr_mask |= 0x20; // set DP mask
break;
}
default:
logerror("mi_reg_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, activecpu_get_pc());
break;
}
}
// RSP Interface
// #define RSP_STATUS_HALT 0x00000001
//static UINT32 rsp_sp_status = 0;
//static UINT32 cpu_sp_status = SP_STATUS_HALT;
static UINT32 sp_mem_addr;
static UINT32 sp_dram_addr;
static int sp_dma_length;
static int sp_dma_count;
static int sp_dma_skip;
static UINT32 sp_semaphore;
static void sp_dma(int direction)
{
UINT8 *src, *dst;
int i;
//int cpu = cpu_getactivecpu();
if (sp_dma_length == 0)
{
return;
}
sp_dma_length++;
if ((sp_dma_length & 3) != 0)
{
//fatalerror("sp_dma (%s): sp_dma_length unaligned %08X\n", cpu ? "RSP" : "R4300i", sp_dma_length);
sp_dma_length = sp_dma_length & ~3;
//sp_dma_length = (sp_dma_length + 3) & ~3;
//sp_dma_length &= ~3;
}
if (sp_mem_addr & 0x3)
{
//sp_mem_addr = (sp_mem_addr + 3) & ~3;
sp_mem_addr = sp_mem_addr & ~3;
// sp_mem_addr &= ~0x3;
// fatalerror("sp_dma (%s): sp_mem_addr unaligned: %08X\n", cpu ? "RSP" : "R4300i", sp_mem_addr);
}
if (sp_dram_addr & 0x3)
{
//sp_dram_addr = (sp_dram_addr + 3) & ~3;
sp_dram_addr = sp_dram_addr & ~3;
// sp_dram_addr &= ~0x3;
// fatalerror("sp_dma (%s): sp_dram_addr unaligned: %08X\n", cpu ? "RSP" : "R4300i", sp_dram_addr);
// Diddy Kong Racing does unaligned DMA?
//sp_dram_addr &= ~0x3;
//sp_dram_addr = (sp_dram_addr + 3) & ~0x3;
}
if (sp_dma_count > 0)
{
fatalerror("sp_dma: dma_count = %d\n", sp_dma_count);
}
if (sp_dma_skip > 0)
{
fatalerror("sp_dma: dma_skip = %d\n", sp_dma_skip);
}
if ((sp_mem_addr & 0xfff) + (sp_dma_length) > 0x1000)
{
fatalerror("sp_dma: dma out of memory area: %08X, %08X\n", sp_mem_addr, sp_dma_length);
}
if (direction == 0) // RDRAM -> I/DMEM
{
src = (UINT8*)&rdram[sp_dram_addr / 4];
dst = (sp_mem_addr & 0x1000) ? (UINT8*)&rsp_imem[(sp_mem_addr & 0xfff) / 4] : (UINT8*)&rsp_dmem[(sp_mem_addr & 0xfff) / 4];
//mame_printf_debug("sp_dma: %08X to %08X, length %08X\n", sp_dram_addr, sp_mem_addr, sp_dma_length);
for (i=0; i < sp_dma_length; i++)
{
dst[BYTE4_XOR_BE(i)] = src[BYTE4_XOR_BE(i)];
}
/*dst = (sp_mem_addr & 0x1000) ? (UINT8*)rsp_imem : (UINT8*)rsp_dmem;
for (i=0; i <= sp_dma_length; i++)
{
dst[BYTE4_XOR_BE(sp_mem_addr+i) & 0xfff] = src[BYTE4_XOR_BE(i)];
}*/
sp_mem_addr += sp_dma_length;
sp_dram_addr += sp_dma_length;
}
else // I/DMEM -> RDRAM
{
src = (sp_mem_addr & 0x1000) ? (UINT8*)&rsp_imem[(sp_mem_addr & 0xfff) / 4] : (UINT8*)&rsp_dmem[(sp_mem_addr & 0xfff) / 4];
dst = (UINT8*)&rdram[sp_dram_addr / 4];
// mame_printf_debug("sp_dma: %08X to %08X, length %08X\n", sp_mem_addr, sp_dram_addr, sp_dma_length);
for (i=0; i < sp_dma_length; i++)
{
dst[BYTE4_XOR_BE(i)] = src[BYTE4_XOR_BE(i)];
}
/*src = (sp_mem_addr & 0x1000) ? (UINT8*)rsp_imem : (UINT8*)rsp_dmem;
for (i=0; i <= sp_dma_length; i++)
{
dst[BYTE4_XOR_BE(i)] = src[BYTE4_XOR_BE(sp_mem_addr+i) & 0xfff];
}*/
sp_mem_addr += sp_dma_length;
sp_dram_addr += sp_dma_length;
}
}
void sp_set_status(UINT32 status)
{
if (status & 0x1)
{
//cpu_trigger(6789);
cpunum_set_input_line(1, INPUT_LINE_HALT, ASSERT_LINE);
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_HALT);
//rsp_sp_status |= SP_STATUS_HALT;
}
if (status & 0x2)
{
//rsp_sp_status |= SP_STATUS_BROKE;
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_BROKE);
if (cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & RSP_STATUS_INTR_BREAK)
{
signal_rcp_interrupt(SP_INTERRUPT);
}
}
}
READ32_HANDLER( n64_sp_reg_r )
{
switch (offset)
{
case 0x00/4: // SP_MEM_ADDR_REG
return sp_mem_addr;
case 0x04/4: // SP_DRAM_ADDR_REG
return sp_dram_addr;
case 0x08/4: // SP_RD_LEN_REG
return (sp_dma_skip << 20) | (sp_dma_count << 12) | sp_dma_length;
case 0x10/4: // SP_STATUS_REG
return cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR);
case 0x14/4: // SP_DMA_FULL_REG
return 0;
case 0x18/4: // SP_DMA_BUSY_REG
return 0;
case 0x1c/4: // SP_SEMAPHORE_REG
return sp_semaphore;
case 0x20/4: // DP_CMD_START
case 0x24/4: // DP_CMD_END
case 0x28/4: // DP_CMD_CURRENT
case 0x2c/4: // DP_CMD_STATUS
case 0x30/4: // DP_CMD_CLOCK
case 0x34/4: // DP_CMD_BUSY
case 0x38/4: // DP_CMD_PIPE_BUSY
case 0x3c/4: // DP_CMD_TMEM_BUSY
return 0;
case 0x40000/4: // PC
return cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_PC);
default:
logerror("sp_reg_r: %08X, %08X at %08X\n", offset, mem_mask, activecpu_get_pc());
break;
}
return 0;
}
WRITE32_HANDLER( n64_sp_reg_w )
{
if ((offset & 0x10000) == 0)
{
switch (offset & 0xffff)
{
case 0x00/4: // SP_MEM_ADDR_REG
sp_mem_addr = data;
break;
case 0x04/4: // SP_DRAM_ADDR_REG
sp_dram_addr = data & 0xffffff;
break;
case 0x08/4: // SP_RD_LEN_REG
sp_dma_length = data & 0xfff;
sp_dma_count = (data >> 12) & 0xff;
sp_dma_skip = (data >> 20) & 0xfff;
sp_dma(0);
break;
case 0x0c/4: // SP_WR_LEN_REG
sp_dma_length = data & 0xfff;
sp_dma_count = (data >> 12) & 0xff;
sp_dma_skip = (data >> 20) & 0xfff;
sp_dma(1);
break;
case 0x10/4: // RSP_STATUS_REG
{
// printf( "RSP_STATUS_REG Write; %08x\n", data );
if (data & 0x00000001) // clear halt
{
//if (first_rsp)
//{
// cpu_spinuntil_trigger(6789);
// printf( "Clearing RSP_STATUS_HALT\n" );
cpunum_set_input_line(1, INPUT_LINE_HALT, CLEAR_LINE);
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_HALT );
// RSP_STATUS &= ~RSP_STATUS_HALT;
//}
//else
//{
// first_rsp = 1;
//}
}
if (data & 0x00000002) // set halt
{
// printf( "Setting RSP_STATUS_HALT\n" );
cpunum_set_input_line(1, INPUT_LINE_HALT, ASSERT_LINE);
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_HALT );
// RSP_STATUS |= RSP_STATUS_HALT;
}
if (data & 0x00000004)
{
//printf( "Clearing RSP_STATUS_BROKE\n" );
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_BROKE );
// RSP_STATUS &= ~RSP_STATUS_BROKE; // clear broke
}
if (data & 0x00000008) // clear interrupt
{
clear_rcp_interrupt(SP_INTERRUPT);
}
if (data & 0x00000010) // set interrupt
{
signal_rcp_interrupt(SP_INTERRUPT);
}
if (data & 0x00000020)
{
// printf( "Clearing RSP_STATUS_SSTEP\n" );
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_SSTEP );
// RSP_STATUS &= ~RSP_STATUS_SSTEP; // clear single step
}
if (data & 0x00000040)
{
//printf( "Setting RSP_STATUS_SSTEP\n" );
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_SSTEP );
// RSP_STATUS |= RSP_STATUS_SSTEP; // set single step
}
if (data & 0x00000080)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_INTR_BREAK );
// RSP_STATUS &= ~RSP_STATUS_INTR_BREAK; // clear interrupt on break
}
if (data & 0x00000100)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_INTR_BREAK );
// RSP_STATUS |= RSP_STATUS_INTR_BREAK; // set interrupt on break
}
if (data & 0x00000200)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_SIGNAL0 );
// RSP_STATUS &= ~RSP_STATUS_SIGNAL0; // clear signal 0
}
if (data & 0x00000400)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_SIGNAL0 );
// RSP_STATUS |= RSP_STATUS_SIGNAL0; // set signal 0
}
if (data & 0x00000800)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_SIGNAL1 );
// RSP_STATUS &= ~RSP_STATUS_SIGNAL1; // clear signal 1
}
if (data & 0x00001000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_SIGNAL1 );
// RSP_STATUS |= RSP_STATUS_SIGNAL1; // set signal 1
}
if (data & 0x00002000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_SIGNAL2 );
// RSP_STATUS &= ~RSP_STATUS_SIGNAL2; // clear signal 2
}
if (data & 0x00004000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_SIGNAL2 );
// RSP_STATUS |= RSP_STATUS_SIGNAL2; // set signal 2
}
if (data & 0x00008000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_SIGNAL3 );
// RSP_STATUS &= ~RSP_STATUS_SIGNAL3; // clear signal 3
}
if (data & 0x00010000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_SIGNAL3 );
// RSP_STATUS |= RSP_STATUS_SIGNAL3; // set signal 3
}
if (data & 0x00020000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_SIGNAL4 );
// RSP_STATUS &= ~RSP_STATUS_SIGNAL4; // clear signal 4
}
if (data & 0x00040000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_SIGNAL4 );
// RSP_STATUS |= RSP_STATUS_SIGNAL4; // set signal 4
}
if (data & 0x00080000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_SIGNAL5 );
// RSP_STATUS &= ~RSP_STATUS_SIGNAL5; // clear signal 5
}
if (data & 0x00100000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_SIGNAL5 );
// RSP_STATUS |= RSP_STATUS_SIGNAL5; // set signal 5
}
if (data & 0x00200000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_SIGNAL6 );
// RSP_STATUS &= ~RSP_STATUS_SIGNAL6; // clear signal 6
}
if (data & 0x00400000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_SIGNAL6 );
// RSP_STATUS |= RSP_STATUS_SIGNAL6; // set signal 6
}
if (data & 0x00800000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) & ~RSP_STATUS_SIGNAL7 );
// RSP_STATUS &= ~RSP_STATUS_SIGNAL7; // clear signal 7
}
if (data & 0x01000000)
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_SR, cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_SR) | RSP_STATUS_SIGNAL7 );
// RSP_STATUS |= RSP_STATUS_SIGNAL7; // set signal 7
}
break;
}
case 0x1c/4: // SP_SEMAPHORE_REG
sp_semaphore = data;
// mame_printf_debug("sp_semaphore = %08X\n", sp_semaphore);
break;
default:
logerror("sp_reg_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, activecpu_get_pc());
break;
}
}
else
{
switch (offset & 0xffff)
{
case 0x00/4: // SP_PC_REG
//printf( "Setting PC to: %08x\n", 0x04001000 | (data & 0xfff ) );
if( cpunum_get_info_int(1, CPUINFO_INT_REGISTER + RSP_NEXTPC) != 0xffffffff )
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_NEXTPC, 0x04001000 | (data & 0xfff));
}
else
{
cpunum_set_info_int(1, CPUINFO_INT_REGISTER + RSP_PC, 0x04001000 | (data & 0xfff));
}
break;
default:
logerror("sp_reg_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, activecpu_get_pc());
break;
}
}
}
UINT32 sp_read_reg(UINT32 reg)
{
switch (reg)
{
//case 4: return rsp_sp_status;
default: return n64_sp_reg_r(reg, 0x00000000);
}
}
void sp_write_reg(UINT32 reg, UINT32 data)
{
switch (reg)
{
default: n64_sp_reg_w(reg, data, 0x00000000); break;
}
}
// RDP Interface
UINT32 dp_start;
UINT32 dp_end;
UINT32 dp_current;
UINT32 dp_status = 0;
void dp_full_sync(void)
{
signal_rcp_interrupt(DP_INTERRUPT);
}
READ32_HANDLER( n64_dp_reg_r )
{
switch (offset)
{
case 0x00/4: // DP_START_REG
return dp_start;
case 0x04/4: // DP_END_REG
return dp_end;
case 0x08/4: // DP_CURRENT_REG
return dp_current;
case 0x0c/4: // DP_STATUS_REG
return dp_status;
default:
logerror("dp_reg_r: %08X, %08X at %08X\n", offset, mem_mask, activecpu_get_pc());
break;
}
return 0;
}
WRITE32_HANDLER( n64_dp_reg_w )
{
switch (offset)
{
case 0x00/4: // DP_START_REG
dp_start = data;
dp_current = dp_start;
break;
case 0x04/4: // DP_END_REG
dp_end = data;
rdp_process_list();
break;
case 0x0c/4: // DP_STATUS_REG
if (data & 0x00000001) dp_status &= ~DP_STATUS_XBUS_DMA;
if (data & 0x00000002) dp_status |= DP_STATUS_XBUS_DMA;
if (data & 0x00000004) dp_status &= ~DP_STATUS_FREEZE;
if (data & 0x00000008) dp_status |= DP_STATUS_FREEZE;
if (data & 0x00000010) dp_status &= ~DP_STATUS_FLUSH;
if (data & 0x00000020) dp_status |= DP_STATUS_FLUSH;
break;
default:
logerror("dp_reg_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, activecpu_get_pc());
break;
}
}
// Video Interface
static UINT32 vi_width;
UINT32 vi_origin;
UINT32 vi_control;
static UINT32 vi_burst, vi_vsync, vi_hsync, vi_leap, vi_hstart, vi_vstart;
static UINT32 vi_intr, vi_vburst, vi_xscale, vi_yscale;
READ32_HANDLER( n64_vi_reg_r )
{
switch (offset)
{
case 0x04/4: // VI_ORIGIN_REG
return vi_origin;
case 0x08/4: // VI_WIDTH_REG
return vi_width;
case 0x0c/4:
return vi_intr;
case 0x10/4: // VI_CURRENT_REG
return video_screen_get_vpos(0);
case 0x14/4: // VI_BURST_REG
return vi_burst;
case 0x18/4: // VI_V_SYNC_REG
return vi_vsync;
case 0x1c/4: // VI_H_SYNC_REG
return vi_hsync;
case 0x20/4: // VI_LEAP_REG
return vi_leap;
case 0x24/4: // VI_H_START_REG
return vi_hstart;
case 0x28/4: // VI_V_START_REG
return vi_vstart;
case 0x2c/4: // VI_V_BURST_REG
return vi_vburst;
case 0x30/4: // VI_X_SCALE_REG
return vi_xscale;
case 0x34/4: // VI_Y_SCALE_REG
return vi_yscale;
default:
logerror("vi_reg_r: %08X, %08X at %08X\n", offset, mem_mask, activecpu_get_pc());
break;
}
return 0;
}
WRITE32_HANDLER( n64_vi_reg_w )
{
switch (offset)
{
case 0x00/4: // VI_CONTROL_REG
if ((vi_control & 0x40) != (data & 0x40))
{
screen_state *state = &Machine->screen[0];
rectangle visarea = state->visarea;
visarea.max_y = (data & 0x40) ? 479 : 239;
video_screen_configure(0, state->width, visarea.max_y + 1, &visarea, Machine->screen[0].refresh);
}
vi_control = data;
break;
case 0x04/4: // VI_ORIGIN_REG
vi_origin = data & 0xffffff;
break;
case 0x08/4: // VI_WIDTH_REG
if (vi_width != data && data > 0)
{
screen_state *state = &Machine->screen[0];
rectangle visarea = state->visarea;
visarea.max_x = data-1;
video_screen_configure(0, visarea.max_x + 1, state->height, &visarea, Machine->screen[0].refresh);
}
vi_width = data;
fb_width = data;
break;
case 0x0c/4: // VI_INTR_REG
vi_intr = data;
break;
case 0x10/4: // VI_CURRENT_REG
clear_rcp_interrupt(VI_INTERRUPT);
break;
case 0x14/4: // VI_BURST_REG
vi_burst = data;
break;
case 0x18/4: // VI_V_SYNC_REG
vi_vsync = data;
break;
case 0x1c/4: // VI_H_SYNC_REG
vi_hsync = data;
break;
case 0x20/4: // VI_LEAP_REG
vi_leap = data;
break;
case 0x24/4: // VI_H_START_REG
vi_hstart = data;
break;
case 0x28/4: // VI_V_START_REG
vi_vstart = data;
break;
case 0x2c/4: // VI_V_BURST_REG
vi_vburst = data;
break;
case 0x30/4: // VI_X_SCALE_REG
vi_xscale = data;
break;
case 0x34/4: // VI_Y_SCALE_REG
vi_yscale = data;
break;
/*
Uncomment this for convenient homebrew debugging
case 0x44/4: // TEMP DEBUG
printf( "E Ping: %08x\n", data );
break;
*/
default:
logerror("vi_reg_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, activecpu_get_pc());
break;
}
}
// Audio Interface
static UINT32 ai_dram_addr;
static UINT32 ai_len;
static UINT32 ai_control = 0;
static int ai_dacrate;
static int ai_bitrate;
static UINT32 ai_status = 0;
static emu_timer *audio_timer;
#define SOUNDBUFFER_LENGTH 0x20000
#define AUDIO_DMA_DEPTH 2
typedef struct
{
UINT32 address;
UINT32 length;
} AUDIO_DMA;
static AUDIO_DMA audio_fifo[AUDIO_DMA_DEPTH];
static int audio_fifo_wpos = 0;
static int audio_fifo_rpos = 0;
static int audio_fifo_num = 0;
static void audio_fifo_push(UINT32 address, UINT32 length)
{
AUDIO_DMA *current;
attotime period;
if (audio_fifo_num == AUDIO_DMA_DEPTH)
{
mame_printf_debug("audio_fifo_push: tried to push to full DMA FIFO!!!\n");
}
// mame_printf_debug("fifo_push: adr %08x len %08x\n", address, length);
audio_fifo[audio_fifo_wpos].address = address;
audio_fifo[audio_fifo_wpos].length = length;
current = &audio_fifo[audio_fifo_wpos];
audio_fifo_wpos++;
audio_fifo_num++;
if (audio_fifo_wpos >= AUDIO_DMA_DEPTH)
{
audio_fifo_wpos = 0;
}
if (audio_fifo_num >= AUDIO_DMA_DEPTH)
{
ai_status |= 0x80000001; // FIFO full
}
// adjust the timer
period = attotime_mul(ATTOTIME_IN_HZ(DACRATE_NTSC), (ai_dacrate + 1) * (current->length / 4));
timer_adjust(audio_timer, period, 0, attotime_zero);
}
static void audio_fifo_pop(void)
{
audio_fifo_rpos++;
audio_fifo_num--;
if (audio_fifo_num < 0)
{
fatalerror("audio_fifo_pop: FIFO underflow!\n");
}
if (audio_fifo_rpos >= AUDIO_DMA_DEPTH)
{
audio_fifo_rpos = 0;
}
if (audio_fifo_num < AUDIO_DMA_DEPTH)
{
ai_status &= ~0x80000001; // FIFO not full
}
ai_len = 0;
}
static AUDIO_DMA *audio_fifo_get_top(void)
{
if (audio_fifo_num > 0)
{
return &audio_fifo[audio_fifo_rpos];
}
else
{
return NULL;
}
}
static void start_audio_dma(void)
{
INT16 *ram = (INT16*)rdram;
AUDIO_DMA *current = audio_fifo_get_top();
ram = &ram[current->address/2];
// mame_printf_debug("DACDMA: %x for %x bytes\n", current->address, current->length);
dmadac_transfer(0, 2, 2, 2, current->length/4, ram);
}
static TIMER_CALLBACK( audio_timer_callback )
{
audio_fifo_pop();
// keep playing if there's another DMA queued
if (audio_fifo_get_top() != NULL)
{
start_audio_dma();
signal_rcp_interrupt(AI_INTERRUPT);
}
}
READ32_HANDLER( n64_ai_reg_r )
{
switch (offset)
{
case 0x04/4: // AI_LEN_REG
{
return ai_len;
}
case 0x0c/4: // AI_STATUS_REG
return ai_status;
default:
logerror("ai_reg_r: %08X, %08X at %08X\n", offset, mem_mask, activecpu_get_pc());
break;
}
return 0;
}
WRITE32_HANDLER( n64_ai_reg_w )
{
// UINT16 *ram = (UINT16*)rdram;
switch (offset)
{
case 0x00/4: // AI_DRAM_ADDR_REG
// mame_printf_debug("ai_dram_addr = %08X at %08X\n", data, activecpu_get_pc());
ai_dram_addr = data & 0xffffff;
break;
case 0x04/4: // AI_LEN_REG
// mame_printf_debug("ai_len = %08X at %08X\n", data, activecpu_get_pc());
ai_len = data & 0x3ffff; // Hardware v2.0 has 18 bits, v1.0 has 15 bits
audio_fifo_push(ai_dram_addr, ai_len);
break;
case 0x08/4: // AI_CONTROL_REG
// mame_printf_debug("ai_control = %08X at %08X\n", data, activecpu_get_pc());
ai_control = data;
break;
case 0x0c/4:
clear_rcp_interrupt(AI_INTERRUPT);
break;
case 0x10/4: // AI_DACRATE_REG
ai_dacrate = data & 0x3fff;
dmadac_set_frequency(0, 2, (double)DACRATE_NTSC / (double)(ai_dacrate+1));
dmadac_enable(0, 2, 1);
break;
case 0x14/4: // AI_BITRATE_REG
// mame_printf_debug("ai_bitrate = %08X\n", data);
ai_bitrate = data & 0xf;
break;
default:
logerror("ai_reg_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, activecpu_get_pc());
break;
}
}
// Peripheral Interface
static UINT32 pi_dram_addr, pi_cart_addr;
static UINT32 pi_first_dma = 1;
READ32_HANDLER( n64_pi_reg_r )
{
switch (offset)
{
case 0x00/4: // PI_DRAM_ADDR_REG
return pi_dram_addr;
case 0x04/4: // PI_CART_ADDR_REG
return pi_cart_addr;
case 0x10/4: // PI_STATUS_REG
return 0;
default:
logerror("pi_reg_r: %08X, %08X at %08X\n", offset, mem_mask, activecpu_get_pc());
break;
}
return 0;
}
WRITE32_HANDLER( n64_pi_reg_w )
{
switch (offset)
{
case 0x00/4: // PI_DRAM_ADDR_REG
{
pi_dram_addr = data;
break;
}
case 0x04/4: // PI_CART_ADDR_REG
{
pi_cart_addr = data;
break;
}
case 0x08/4: // PI_RD_LEN_REG
{
int i;
UINT32 dma_length = (data + 1);
/*if (dma_length & 3)
{
dma_length = (dma_length + 3) & ~3;
}*/
//mame_printf_debug("PI DMA: %08X to %08X, length %08X\n", pi_dram_addr, pi_cart_addr, dma_length);
if (pi_dram_addr != 0xffffffff)
{
for (i=0; i < dma_length; i++)
{
UINT8 b = program_read_byte_32be(pi_dram_addr);
program_write_byte_32be(pi_cart_addr, b);
pi_cart_addr += 1;
pi_dram_addr += 1;
}
}
signal_rcp_interrupt(PI_INTERRUPT);
break;
}
case 0x0c/4: // PI_WR_LEN_REG
{
int i;
UINT32 dma_length = (data + 1);
/*if (dma_length & 3)
{
dma_length = (dma_length + 3) & ~3;
}*/
//mame_printf_debug("PI DMA: %08X to %08X, length %08X\n", pi_cart_addr, pi_dram_addr, dma_length);
if (pi_dram_addr != 0xffffffff)
{
for (i=0; i < dma_length; i++)
{
/*UINT32 d = program_read_dword_32be(pi_cart_addr);
program_write_dword_32be(pi_dram_addr, d);
pi_cart_addr += 4;
pi_dram_addr += 4;*/
UINT8 b = program_read_byte_32be(pi_cart_addr);
program_write_byte_32be(pi_dram_addr, b);
pi_cart_addr += 1;
pi_dram_addr += 1;
}
}
signal_rcp_interrupt(PI_INTERRUPT);
if (pi_first_dma)
{
// TODO: CIC-6105 has different address...
program_write_dword_32be(0x00000318, 0x400000);
program_write_dword_32be(0x000003f0, 0x800000);
pi_first_dma = 0;
}
break;
}
case 0x10/4: // PI_STATUS_REG
{
if (data & 0x2)
{
clear_rcp_interrupt(PI_INTERRUPT);
}
break;
}
default:
logerror("pi_reg_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, activecpu_get_pc());
break;
}
}
// RDRAM Interface
READ32_HANDLER( n64_ri_reg_r )
{
switch (offset)
{
default:
logerror("ri_reg_r: %08X, %08X at %08X\n", offset, mem_mask, activecpu_get_pc());
break;
}
return 0;
}
WRITE32_HANDLER( n64_ri_reg_w )
{
switch (offset)
{
default:
logerror("ri_reg_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, activecpu_get_pc());
break;
}
}
// Serial Interface
static UINT8 pif_ram[0x40];
static UINT8 pif_cmd[0x40];
UINT32 si_dram_addr = 0;
UINT32 si_pif_addr = 0;
UINT32 si_status = 0;
static UINT8 eeprom[512];
static UINT8 mempack[0x8000];
static UINT8 calc_mempack_crc(UINT8 *buffer, int length)
{
int i, j;
UINT32 crc = 0;
UINT32 temp2 = 0;
for (i=0; i <= length; i++)
{
for (j=7; j >= 0; j--)
{
if ((crc & 0x80) != 0)
{
temp2 = 0x85;
}
else
{
temp2 = 0;
}
crc <<= 1;
if (i == length)
{
crc &= 0xff;
}
else
{
if ((buffer[i] & (1 << j)) != 0)
{
crc |= 0x1;
}
}
crc ^= temp2;
}
}
return crc;
}
static int pif_channel_handle_command(int channel, int slength, UINT8 *sdata, int rlength, UINT8 *rdata)
{
int i;
UINT8 command = sdata[0];
switch (command)
{
case 0x00: // Read status
{
if (slength != 1 || rlength != 3)
{
// osd_die("handle_pif: read status (bytes to send %d, bytes to receive %d)\n", bytes_to_send, bytes_to_recv);
}
switch (channel)
{
case 0:
{
rdata[0] = 0x05;
rdata[1] = 0x00;
rdata[2] = 0x02;
return 0;
}
case 1:
case 2:
case 3:
{
// not connected
return 1;
}
case 4:
{
rdata[0] = 0x00;
rdata[1] = 0x80;
rdata[2] = 0x00;
//rdata[0] = 0xff;
//rdata[1] = 0xff;
//rdata[2] = 0xff;
return 1;
}
case 5:
{
mame_printf_debug("EEPROM2? read status\n");
return 1;
}
}
break;
}
case 0x01: // Read button values
{
UINT16 buttons = 0;
INT8 x = 0, y = 0;
if (slength != 1 || rlength != 4)
{
fatalerror("handle_pif: read button values (bytes to send %d, bytes to receive %d)\n", slength, rlength);
}
switch (channel)
{
case 0:
{
buttons = readinputport((channel*3) + 0);
x = readinputport((channel*3) + 1) - 128;
y = readinputport((channel*3) + 2) - 128;
rdata[0] = (buttons >> 8) & 0xff;
rdata[1] = (buttons >> 0) & 0xff;
rdata[2] = (UINT8)(x);
rdata[3] = (UINT8)(y);
return 0;
}
case 1:
case 2:
case 3:
{
// not connected
return 1;
}
}
break;
}
case 0x02:
{
UINT32 address, checksum;
/*mame_printf_debug("Read from mempack, rlength = %d, slength = %d\n", rlength, slength);
for (i=0; i < slength; i++)
{
mame_printf_debug("%02X ", sdata[i]);
}
mame_printf_debug("\n");*/
address = (sdata[1] << 8) | (sdata[2]);
checksum = address & 0x1f;
address &= ~0x1f;
if (address == 0x400)
{
for (i=0; i < rlength-1; i++)
{
rdata[i] = 0x00;
}
rdata[rlength-1] = calc_mempack_crc(rdata, rlength-1);
// mame_printf_debug("CRC = %02X\n", rdata[rlength-1]);
}
else if (address < 0x7fe0)
{
for (i=0; i < rlength-1; i++)
{
rdata[i] = mempack[address+i];
}
rdata[rlength-1] = calc_mempack_crc(rdata, rlength-1);
// mame_printf_debug("CRC = %02X\n", rdata[rlength-1]);
}
return 1;
}
case 0x03:
{
UINT32 address, checksum;
int i;
/*mame_printf_debug("Write to mempack, rlength = %d, slength = %d\n", rlength, slength);
for (i=0; i < slength; i++)
{
mame_printf_debug("%02X ", sdata[i]);
}
mame_printf_debug("\n");*/
address = (sdata[1] << 8) | (sdata[2]);
checksum = address & 0x1f;
address &= ~0x1f;
if (address == 0x8000)
{
}
else
{
for (i=3; i < slength; i++)
{
mempack[address++] = sdata[i];
}
}
rdata[0] = calc_mempack_crc(&sdata[3], slength-3);
return 1;
}
case 0x04: // Read from EEPROM
{
UINT8 block_offset;
if (channel != 4)
{
//fatalerror("Tried to write to EEPROM on channel %d\n", channel);
return 1;
}
if (slength != 2 || rlength != 8)
{
fatalerror("handle_pif: write EEPROM (bytes to send %d, bytes to receive %d)\n", slength, rlength);
}
block_offset = sdata[1] * 8;
for (i=0; i < 8; i++)
{
rdata[i] = eeprom[block_offset+i];
}
return 1;
}
case 0x05: // Write to EEPROM
{
UINT8 block_offset;
if (channel != 4)
{
//fatalerror("Tried to write to EEPROM on channel %d\n", channel);
return 1;
}
if (slength != 10 || rlength != 1)
{
fatalerror("handle_pif: write EEPROM (bytes to send %d, bytes to receive %d)\n", slength, rlength);
}
block_offset = sdata[1] * 8;
//mame_printf_debug("Write EEPROM: offset %02X: ", block_offset);
for (i=0; i < 8; i++)
{
//mame_printf_debug("%02X ", sdata[2+i]);
eeprom[block_offset+i] = sdata[2+i];
}
//mame_printf_debug("\n");
rdata[0] = 0;
return 1;
}
case 0xff: // reset
{
rdata[0] = 0xff;
rdata[1] = 0xff;
rdata[2] = 0xff;
return 0;
}
default:
{
mame_printf_debug("handle_pif: unknown/unimplemented command %02X\n", command);
return 1;
}
}
return 0;
}
static void handle_pif(void)
{
int j;
/*
{
int i;
for (i=0; i < 8; i++)
{
int j = i * 8;
mame_printf_debug("PIFCMD%d: %02X %02X %02X %02X %02X %02X %02X %02X\n", i, pif_cmd[j], pif_cmd[j+1], pif_cmd[j+2], pif_cmd[j+3], pif_cmd[j+4], pif_cmd[j+5], pif_cmd[j+6], pif_cmd[j+7]);
}
mame_printf_debug("\n");
}
*/
if (pif_cmd[0x3f] == 0x1) // only handle the command if the last byte is 1
{
int channel = 0;
int end = 0;
int cmd_ptr = 0;
while (cmd_ptr < 0x3f && !end)
{
UINT8 bytes_to_send;
INT8 bytes_to_recv;
bytes_to_send = pif_cmd[cmd_ptr++];
if (bytes_to_send == 0xfe)
{
end = 1;
}
else if (bytes_to_send == 0xff)
{
// do nothing
}
else
{
if (bytes_to_send > 0 && (bytes_to_send & 0xc0) == 0)
{
int res;
UINT8 recv_buffer[0x40];
UINT8 send_buffer[0x40];
bytes_to_recv = pif_cmd[cmd_ptr++];
for (j=0; j < bytes_to_send; j++)
{
send_buffer[j] = pif_cmd[cmd_ptr++];
}
res = pif_channel_handle_command(channel, bytes_to_send, send_buffer, bytes_to_recv, recv_buffer);
if (res == 0)
{
if (cmd_ptr + bytes_to_recv > 0x3f)
{
fatalerror("cmd_ptr overflow\n");
}
for (j=0; j < bytes_to_recv; j++)
{
pif_ram[cmd_ptr++] = recv_buffer[j];
}
}
else if (res == 1)
{
int offset = 0;//bytes_to_send;
pif_ram[cmd_ptr-offset-2] |= 0x80;
}
}
channel++;
}
}
pif_ram[0x3f] = 0;
}
/*
{
int i;
for (i=0; i < 8; i++)
{
int j = i * 8;
mame_printf_debug("PIFRAM%d: %02X %02X %02X %02X %02X %02X %02X %02X\n", i, pif_ram[j], pif_ram[j+1], pif_ram[j+2], pif_ram[j+3], pif_ram[j+4], pif_ram[j+5], pif_ram[j+6], pif_ram[j+7]);
}
mame_printf_debug("\n");
}
*/
}
static void pif_dma(int direction)
{
int i;
UINT32 *src, *dst;
if (si_dram_addr & 0x3)
{
fatalerror("pif_dma: si_dram_addr unaligned: %08X\n", si_dram_addr);
}
if (direction) // RDRAM -> PIF RAM
{
src = (UINT32*)&rdram[(si_dram_addr & 0x1fffffff) / 4];
for (i=0; i < 64; i+=4)
{
UINT32 d = *src++;
pif_ram[i+0] = (d >> 24) & 0xff;
pif_ram[i+1] = (d >> 16) & 0xff;
pif_ram[i+2] = (d >> 8) & 0xff;
pif_ram[i+3] = (d >> 0) & 0xff;
}
memcpy(pif_cmd, pif_ram, 0x40);
}
else // PIF RAM -> RDRAM
{
handle_pif();
dst = (UINT32*)&rdram[(si_dram_addr & 0x1fffffff) / 4];
for (i=0; i < 64; i+=4)
{
UINT32 d = 0;
d |= pif_ram[i+0] << 24;
d |= pif_ram[i+1] << 16;
d |= pif_ram[i+2] << 8;
d |= pif_ram[i+3] << 0;
*dst++ = d;
}
}
si_status |= 0x1000;
signal_rcp_interrupt(SI_INTERRUPT);
}
READ32_HANDLER( n64_si_reg_r )
{
switch (offset)
{
case 0x00/4: // SI_DRAM_ADDR_REG
return si_dram_addr;
case 0x18/4: // SI_STATUS_REG
return si_status;
}
return 0;
}
WRITE32_HANDLER( n64_si_reg_w )
{
switch (offset)
{
case 0x00/4: // SI_DRAM_ADDR_REG
si_dram_addr = data;
// mame_printf_debug("si_dram_addr = %08X\n", si_dram_addr);
break;
case 0x04/4: // SI_PIF_ADDR_RD64B_REG
// PIF RAM -> RDRAM
si_pif_addr = data;
pif_dma(0);
break;
case 0x10/4: // SI_PIF_ADDR_WR64B_REG
// RDRAM -> PIF RAM
si_pif_addr = data;
pif_dma(1);
break;
case 0x18/4: // SI_STATUS_REG
si_status &= ~0x1000;
clear_rcp_interrupt(SI_INTERRUPT);
break;
default:
logerror("si_reg_w: %08X, %08X, %08X\n", data, offset, mem_mask);
break;
}
}
UINT32 cic_status = 0x00000000;
READ32_HANDLER( n64_pif_ram_r )
{
/*mame_printf_debug( "pif_ram_r: %08X, %08X = %08X\n", offset << 2, mem_mask, ( ( pif_ram[offset*4+0] << 24 ) | ( pif_ram[offset*4+1] << 16 ) | ( pif_ram[offset*4+2] << 8 ) | ( pif_ram[offset*4+3] << 0 ) ) & ~mem_mask );*/
if( offset == ( 0x24 / 4 ) )
{
cic_status = 0x00000080;
}
if( offset == ( 0x3C / 4 ) )
{
return cic_status;
}
return ( ( pif_ram[offset*4+0] << 24 ) | ( pif_ram[offset*4+1] << 16 ) | ( pif_ram[offset*4+2] << 8 ) | ( pif_ram[offset*4+3] << 0 ) ) & ~mem_mask;
}
WRITE32_HANDLER( n64_pif_ram_w )
{
/*mame_printf_debug("pif_ram_w: %08X, %08X, %08X\n", data, offset << 4, mem_mask);*/
/*
if( (~mem_mask) & 0xff000000 )
{
pif_ram[offset*4+0] = ( data >> 24 ) & 0x000000ff;
}
if( (~mem_mask) & 0x00ff0000 )
{
pif_ram[offset*4+1] = ( data >> 16 ) & 0x000000ff;
}
if( (~mem_mask) & 0x0000ff00 )
{
pif_ram[offset*4+2] = ( data >> 8 ) & 0x000000ff;
}
if( (~mem_mask) & 0x000000ff )
{
pif_ram[offset*4+3] = ( data >> 0 ) & 0x000000ff;
}
*/
signal_rcp_interrupt(SI_INTERRUPT);
}
//static UINT16 crc_seed = 0x3f;
void n64_machine_reset(void)
{
int i;
//UINT32 *pif_rom = (UINT32*)memory_region(REGION_USER1);
UINT32 *cart = (UINT32*)memory_region(REGION_USER2);
UINT64 boot_checksum;
cpunum_set_info_int(0, CPUINFO_INT_MIPS3_DRC_OPTIONS, MIPS3DRC_FASTEST_OPTIONS + MIPS3DRC_STRICT_VERIFY);
/* configure fast RAM regions for DRC */
cpunum_set_info_int(0, CPUINFO_INT_MIPS3_FASTRAM_SELECT, 0);
cpunum_set_info_int(0, CPUINFO_INT_MIPS3_FASTRAM_START, 0x00000000);
cpunum_set_info_int(0, CPUINFO_INT_MIPS3_FASTRAM_END, 0x007fffff);
cpunum_set_info_ptr(0, CPUINFO_PTR_MIPS3_FASTRAM_BASE, rdram);
cpunum_set_info_int(0, CPUINFO_INT_MIPS3_FASTRAM_READONLY, 0);
audio_timer = timer_alloc(audio_timer_callback);
timer_adjust(audio_timer, attotime_never, 0, attotime_never);
cpunum_set_input_line(1, INPUT_LINE_HALT, ASSERT_LINE);
// bootcode differs between CIC-chips, so we can use its checksum to detect the CIC-chip
boot_checksum = 0;
for (i=0x40; i < 0x1000; i+=4)
{
boot_checksum += cart[i/4]+i;
}
if (boot_checksum == U64(0x000000d057e84864))
{
// CIC-NUS-6101
mame_printf_debug("CIC-NUS-6101 detected\n");
// crc_seed = 0x3f;
pif_ram[0x24] = 0x00;
pif_ram[0x25] = 0x04;
pif_ram[0x26] = 0x3f;
pif_ram[0x27] = 0x3f;
}
else if (boot_checksum == U64(0x000000d6499e376b))
{
// CIC-NUS-6103
mame_printf_debug("CIC-NUS-6103 detected\n");
// crc_seed = 0x78;
pif_ram[0x24] = 0x00;
pif_ram[0x25] = 0x04;
pif_ram[0x26] = 0x78;
pif_ram[0x27] = 0x78;
}
else if (boot_checksum == U64(0x0000011a4a1604b6))
{
// CIC-NUS-6105
mame_printf_debug("CIC-NUS-6105 detected\n");
// crc_seed = 0x91;
// first_rsp = 0;
pif_ram[0x24] = 0x00;
pif_ram[0x25] = 0x04;
pif_ram[0x26] = 0x91;
pif_ram[0x27] = 0x91;
}
else if (boot_checksum == U64(0x000000d6d5de4ba0))
{
// CIC-NUS-6106
mame_printf_debug("CIC-NUS-6106 detected\n");
// crc_seed = 0x85;
pif_ram[0x24] = 0x00;
pif_ram[0x25] = 0x04;
pif_ram[0x26] = 0x85;
pif_ram[0x27] = 0x85;
}
else
{
mame_printf_debug("Unknown BootCode Checksum %08X%08X\n", (UINT32)(boot_checksum>>32),(UINT32)(boot_checksum));
}
/*
// bootcode differs between CIC-chips, so we can use its checksum to detect the CIC-chip
boot_checksum = 0;
for (i=0x40; i < 0x1000; i+=4)
{
boot_checksum += cart[i/4]+i;
}
if (boot_checksum == U64(0x000000d057e84864))
{
// CIC-NUS-6101
mame_printf_debug("CIC-NUS-6101 detected\n");
crc_seed = 0x3f;
}
else if (boot_checksum == U64(0x000000d6499e376b))
{
// CIC-NUS-6103
mame_printf_debug("CIC-NUS-6103 detected\n");
crc_seed = 0x78;
}
else if (boot_checksum == U64(0x0000011a4a1604b6))
{
// CIC-NUS-6105
mame_printf_debug("CIC-NUS-6105 detected\n");
crc_seed = 0x91;
first_rsp = 0;
}
else if (boot_checksum == U64(0x000000d6d5de4ba0))
{
// CIC-NUS-6106
mame_printf_debug("CIC-NUS-6106 detected\n");
crc_seed = 0x85;
}
else
{
mame_printf_debug("Unknown BootCode Checksum %08X%08X\n", (UINT32)(boot_checksum>>32),(UINT32)(boot_checksum));
}
// The PIF Boot ROM is not dumped, the following code simulates it
// clear all registers
for (i=1; i < 32; i++)
{
*pif_rom++ = 0x00000000 | 0 << 21 | 0 << 16 | i << 11 | 0x20; // ADD ri, r0, r0
}
// R20 <- 0x00000001
*pif_rom++ = 0x34000000 | 20 << 16 | 0x0001; // ORI r20, r0, 0x0001
// R22 <- 0x0000003F
*pif_rom++ = 0x34000000 | 22 << 16 | crc_seed; // ORI r22, r0, 0x003f
// R29 <- 0xA4001FF0
*pif_rom++ = 0x3c000000 | 29 << 16 | 0xa400; // LUI r29, 0xa400
*pif_rom++ = 0x34000000 | 29 << 21 | 29 << 16 | 0x1ff0; // ORI r29, r29, 0x1ff0
// clear CP0 registers
for (i=0; i < 32; i++)
{
*pif_rom++ = 0x40000000 | 4 << 21 | 0 << 16 | i << 11; // MTC2 cp0ri, r0
}
// Random <- 0x0000001F
*pif_rom++ = 0x34000000 | 1 << 16 | 0x001f;
*pif_rom++ = 0x40000000 | 4 << 21 | 1 << 16 | 1 << 11; // MTC2 Random, r1
// Status <- 0x70400004
*pif_rom++ = 0x3c000000 | 1 << 16 | 0x7040; // LUI r1, 0x7040
*pif_rom++ = 0x34000000 | 1 << 21 | 1 << 16 | 0x0004; // ORI r1, r1, 0x0004
*pif_rom++ = 0x40000000 | 4 << 21 | 1 << 16 | 12 << 11; // MTC2 Status, r1
// PRId <- 0x00000B00
*pif_rom++ = 0x34000000 | 1 << 16 | 0x0b00; // ORI r1, r0, 0x0b00
*pif_rom++ = 0x40000000 | 4 << 21 | 1 << 16 | 15 << 11; // MTC2 PRId, r1
// Config <- 0x0006E463
*pif_rom++ = 0x3c000000 | 1 << 16 | 0x0006; // LUI r1, 0x0006
*pif_rom++ = 0x34000000 | 1 << 21 | 1 << 16 | 0xe463; // ORI r1, r1, 0xe463
*pif_rom++ = 0x40000000 | 4 << 21 | 1 << 16 | 16 << 11; // MTC2 Config, r1
// (0xa4300004) <- 0x01010101
*pif_rom++ = 0x3c000000 | 1 << 16 | 0x0101; // LUI r1, 0x0101
*pif_rom++ = 0x34000000 | 1 << 21 | 1 << 16 | 0x0101; // ORI r1, r1, 0x0101
*pif_rom++ = 0x3c000000 | 3 << 16 | 0xa430; // LUI r3, 0xa430
*pif_rom++ = 0xac000000 | 3 << 21 | 1 << 16 | 0x0004; // SW r1, 0x0004(r3)
// Copy 0xb0000000...1fff -> 0xa4000000...1fff
*pif_rom++ = 0x34000000 | 3 << 16 | 0x0400; // ORI r3, r0, 0x0400
*pif_rom++ = 0x3c000000 | 4 << 16 | 0xb000; // LUI r4, 0xb000
*pif_rom++ = 0x3c000000 | 5 << 16 | 0xa400; // LUI r5, 0xa400
*pif_rom++ = 0x8c000000 | 4 << 21 | 1 << 16; // LW r1, 0x0000(r4)
*pif_rom++ = 0xac000000 | 5 << 21 | 1 << 16; // SW r1, 0x0000(r5)
*pif_rom++ = 0x20000000 | 4 << 21 | 4 << 16 | 0x0004; // ADDI r4, r4, 0x0004
*pif_rom++ = 0x20000000 | 5 << 21 | 5 << 16 | 0x0004; // ADDI r5, r5, 0x0004
*pif_rom++ = 0x20000000 | 3 << 21 | 3 << 16 | 0xffff; // ADDI r3, r3, -1
*pif_rom++ = 0x14000000 | 3 << 21 | 0 << 16 | 0xfffa; // BNE r3, r0, -6
*pif_rom++ = 0x00000000;
*pif_rom++ = 0x34000000 | 3 << 16 | 0x0000; // ORI r3, r0, 0x0000
*pif_rom++ = 0x34000000 | 4 << 16 | 0x0000; // ORI r4, r0, 0x0000
*pif_rom++ = 0x34000000 | 5 << 16 | 0x0000; // ORI r5, r0, 0x0000
// Zelda and DK64 need these
*pif_rom++ = 0x3c000000 | 9 << 16 | 0xa400;
*pif_rom++ = 0x34000000 | 9 << 21 | 9 << 16 | 0x1ff0;
*pif_rom++ = 0x3c000000 | 11 << 16 | 0xa400;
*pif_rom++ = 0x3c000000 | 31 << 16 | 0xffff;
*pif_rom++ = 0x34000000 | 31 << 21 | 31 << 16 | 0xffff;
*pif_rom++ = 0x3c000000 | 1 << 16 | 0xa400; // LUI r1, 0xa400
*pif_rom++ = 0x34000000 | 1 << 21 | 1 << 16 | 0x0040; // ORI r1, r1, 0x0040
*pif_rom++ = 0x00000000 | 1 << 21 | 0x8; // JR r1
*/
}