/* 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 UINT32 vi_width; UINT32 vi_origin; UINT32 vi_control; UINT32 vi_burst, vi_vsync, vi_hsync, vi_leap, vi_hstart, vi_vstart; 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 mame_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; mame_time 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 = scale_up_mame_time(MAME_TIME_IN_HZ(DACRATE_NTSC), (ai_dacrate + 1) * (current->length / 4)); mame_timer_adjust(audio_timer, period, 0, time_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 UINT8 pif_ram[0x40]; 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 = mame_timer_alloc(audio_timer_callback); mame_timer_adjust(audio_timer, time_never, 0, time_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 */ }