2021-03-16 07:17:43 +00:00
|
|
|
|
// Project64 - A Nintendo 64 emulator
|
2021-05-18 11:51:36 +00:00
|
|
|
|
// https://www.pj64-emu.com/
|
2021-03-16 07:17:43 +00:00
|
|
|
|
// Copyright(C) 2001-2021 Project64
|
|
|
|
|
// Copyright(C) 2012 Bobby Smiles
|
|
|
|
|
// Copyright(C) 2009 Richard Goedeken
|
|
|
|
|
// Copyright(C) 2002 Hacktarux
|
|
|
|
|
// GNU/GPLv2 licensed: https://gnu.org/licenses/gpl-2.0.html
|
|
|
|
|
|
2016-04-28 07:09:44 +00:00
|
|
|
|
#include "stdafx.h"
|
|
|
|
|
#include "mem.h"
|
|
|
|
|
#include "ucodes.h"
|
|
|
|
|
#include <memory.h>
|
|
|
|
|
|
|
|
|
|
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
|
|
|
|
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// Helper functions prototypes
|
|
|
|
|
|
2016-04-28 07:09:44 +00:00
|
|
|
|
static unsigned int sum_bytes(const uint8_t *bytes, uint32_t size);
|
|
|
|
|
|
|
|
|
|
CHle::CHle(const RSP_INFO & Rsp_Info) :
|
|
|
|
|
m_dram(Rsp_Info.RDRAM),
|
|
|
|
|
m_dmem(Rsp_Info.DMEM),
|
|
|
|
|
m_imem(Rsp_Info.IMEM),
|
|
|
|
|
m_mi_intr(Rsp_Info.MI_INTR_REG),
|
|
|
|
|
m_sp_mem_addr(Rsp_Info.SP_MEM_ADDR_REG),
|
|
|
|
|
m_sp_dram_addr(Rsp_Info.SP_DRAM_ADDR_REG),
|
|
|
|
|
m_sp_rd_length(Rsp_Info.SP_RD_LEN_REG),
|
|
|
|
|
m_sp_wr_length(Rsp_Info.SP_WR_LEN_REG),
|
|
|
|
|
m_sp_status(Rsp_Info.SP_STATUS_REG),
|
|
|
|
|
m_sp_dma_full(Rsp_Info.SP_DMA_FULL_REG),
|
|
|
|
|
m_sp_dma_busy(Rsp_Info.SP_DMA_BUSY_REG),
|
|
|
|
|
m_sp_pc(Rsp_Info.SP_PC_REG),
|
|
|
|
|
m_sp_semaphore(Rsp_Info.SP_SEMAPHORE_REG),
|
|
|
|
|
m_dpc_start(Rsp_Info.DPC_START_REG),
|
|
|
|
|
m_dpc_end(Rsp_Info.DPC_END_REG),
|
|
|
|
|
m_dpc_current(Rsp_Info.DPC_CURRENT_REG),
|
|
|
|
|
m_dpc_status(Rsp_Info.DPC_STATUS_REG),
|
|
|
|
|
m_dpc_clock(Rsp_Info.DPC_CLOCK_REG),
|
|
|
|
|
m_dpc_bufbusy(Rsp_Info.DPC_BUFBUSY_REG),
|
|
|
|
|
m_dpc_pipebusy(Rsp_Info.DPC_PIPEBUSY_REG),
|
|
|
|
|
m_dpc_tmem(Rsp_Info.DPC_TMEM_REG),
|
|
|
|
|
m_CheckInterrupts(Rsp_Info.CheckInterrupts),
|
|
|
|
|
m_ProcessDList(Rsp_Info.ProcessDList),
|
|
|
|
|
m_ProcessAList(Rsp_Info.ProcessAList),
|
|
|
|
|
m_ProcessRdpList(Rsp_Info.ProcessRdpList),
|
|
|
|
|
m_ShowCFB(Rsp_Info.ShowCFB),
|
|
|
|
|
m_AudioHle(false),
|
|
|
|
|
m_GraphicsHle(true),
|
|
|
|
|
m_ForwardAudio(false),
|
|
|
|
|
m_ForwardGFX(true)
|
|
|
|
|
{
|
|
|
|
|
//m_AudioHle = ReadCfgInt("Settings", "AudioHle", false);
|
|
|
|
|
//m_GraphicsHle = ReadCfgInt("Settings", "GraphicsHle", true);
|
|
|
|
|
memset(&m_alist_buffer, 0, sizeof(m_alist_buffer));
|
|
|
|
|
memset(&m_alist_audio, 0, sizeof(m_alist_audio));
|
|
|
|
|
memset(&m_alist_naudio, 0, sizeof(m_alist_naudio));
|
|
|
|
|
memset(&m_alist_nead, 0, sizeof(m_alist_nead));
|
|
|
|
|
memset(&m_mp3_buffer, 0, sizeof(m_mp3_buffer));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHle::~CHle()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHle::rsp_break(unsigned int setbits)
|
|
|
|
|
{
|
|
|
|
|
*m_sp_status |= setbits | SP_STATUS_BROKE | SP_STATUS_HALT;
|
|
|
|
|
|
|
|
|
|
if ((*m_sp_status & SP_STATUS_INTR_BREAK))
|
|
|
|
|
{
|
|
|
|
|
*m_mi_intr |= MI_INTR_SP;
|
|
|
|
|
m_CheckInterrupts();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHle::hle_execute(void)
|
|
|
|
|
{
|
|
|
|
|
if (is_task())
|
|
|
|
|
{
|
|
|
|
|
if (!try_fast_task_dispatching())
|
|
|
|
|
{
|
|
|
|
|
normal_task_dispatching();
|
|
|
|
|
}
|
|
|
|
|
rsp_break(SP_STATUS_SIG2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
non_task_dispatching();
|
|
|
|
|
rsp_break(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// Local functions
|
|
|
|
|
|
2016-04-28 07:09:44 +00:00
|
|
|
|
static unsigned int sum_bytes(const uint8_t * bytes, unsigned int size)
|
|
|
|
|
{
|
|
|
|
|
unsigned int sum = 0;
|
|
|
|
|
const unsigned char *const bytes_end = bytes + size;
|
|
|
|
|
|
|
|
|
|
while (bytes != bytes_end)
|
|
|
|
|
{
|
|
|
|
|
sum += *bytes++;
|
|
|
|
|
}
|
|
|
|
|
return sum;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 05:29:28 +00:00
|
|
|
|
/*
|
|
|
|
|
TODO:
|
|
|
|
|
|
|
|
|
|
Try to figure if the RSP was launched using osSpTask* functions
|
|
|
|
|
and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless).
|
|
|
|
|
|
|
|
|
|
Previously, the ucode_size field was used to determine this,
|
|
|
|
|
but it is not robust enough (hi Pok<EFBFBD>mon Stadium!) because games could write anything
|
|
|
|
|
in this field: most ucode_boot discard the value and just use 0xf7f anyway.
|
|
|
|
|
|
|
|
|
|
Using ucode_boot_size should be more robust in this regard.
|
|
|
|
|
*/
|
|
|
|
|
|
2016-04-28 07:09:44 +00:00
|
|
|
|
bool CHle::is_task(void)
|
|
|
|
|
{
|
|
|
|
|
return (*dmem_u32(this, TASK_UCODE_BOOT_SIZE) <= 0x1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CHle::try_fast_task_dispatching(void)
|
|
|
|
|
{
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// Identify task microcode by its type
|
2016-04-28 07:09:44 +00:00
|
|
|
|
switch (*dmem_u32(this, TASK_TYPE))
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
if (m_ForwardGFX)
|
|
|
|
|
{
|
|
|
|
|
m_ProcessDList();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
if (m_AudioHle)
|
|
|
|
|
{
|
|
|
|
|
m_ProcessAList();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (try_fast_audio_dispatching())
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 7:
|
|
|
|
|
m_ShowCFB();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CHle::try_fast_audio_dispatching(void)
|
|
|
|
|
{
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// Identify audio microcode by using the content of ucode_data
|
2016-04-28 07:09:44 +00:00
|
|
|
|
uint32_t ucode_data = *dmem_u32(this, TASK_UCODE_DATA);
|
|
|
|
|
uint32_t v;
|
|
|
|
|
|
|
|
|
|
if (*dram_u32(this, ucode_data) == 0x00000001)
|
|
|
|
|
{
|
|
|
|
|
if (*dram_u32(this, ucode_data + 0x30) == 0xf0000f00)
|
|
|
|
|
{
|
|
|
|
|
v = *dram_u32(this, ucode_data + 0x28);
|
|
|
|
|
switch (v)
|
|
|
|
|
{
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1e24138c: // Audio ABI (most common)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_audio(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1dc8138c: // GoldenEye 007
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_audio_ge(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1e3c1390: // Blast Corp, Diddy Kong Racing
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_audio_bc(this);
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
WarnMessage("ABI1 identification regression: v=%08x", v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
v = *dram_u32(this, ucode_data + 0x10);
|
|
|
|
|
switch (v)
|
|
|
|
|
{
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x11181350: // Mario Kart, Wave Race (E)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_mk(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x111812e0: // StarFox 64 (J)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_sfj(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x110412ac: // Wave Race (J Rev B)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_wrjb(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x110412cc: // StarFox/LylatWars (except J)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_sf(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1cd01250: // F-Zero X
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_fz(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1f08122c: // Yoshi's Story
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_ys(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1f38122c: // 1080<38> Snowboarding
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_1080(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1f681230: // Zelda Ocarina of Time / Zelda Majora's Mask (J, J Rev A)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_oot(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1f801250: // Zelda Majora's Mask (except J, J Rev A, E Beta), Pok<6F>mon Stadium 2
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_mm(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x109411f8: // Zelda Majora's Mask (E Beta)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_mmb(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1eac11b8: // Animal Crossing
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_nead_ac(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x00010010: // MusyX v2 (Indiana Jones, Battle For Naboo)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
musyx_v2_task(this);
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
WarnMessage("ABI2 identification regression: v=%08x", v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
v = *dram_u32(this, ucode_data + 0x10);
|
|
|
|
|
switch (v)
|
|
|
|
|
{
|
|
|
|
|
case 0x00000001: /* MusyX v1
|
2021-03-16 05:29:28 +00:00
|
|
|
|
Rogue Squadron, Resident Evil 2, Polaris Sno Cross,
|
|
|
|
|
The World Is Not Enough, Rugrats In Paris, NBA Show Time,
|
|
|
|
|
Hydro Thunder, Tarzan, Gauntlet Legend, Rush 2049 */
|
2016-04-28 07:09:44 +00:00
|
|
|
|
musyx_v1_task(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x0000127c: // naudio (many games)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_naudio(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x00001280: // Banjo Kazooie
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_naudio_bk(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1c58126c: // Donkey Kong
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_naudio_dk(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1ae8143c: // Banjo Tooie, Jet Force Gemini, Mickey SpeedWay USA, Perfect Dark
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_naudio_mp3(this);
|
|
|
|
|
return true;
|
2021-03-16 05:29:28 +00:00
|
|
|
|
case 0x1ab0140c: // Conker's Bad Fur Day
|
2016-04-28 07:09:44 +00:00
|
|
|
|
alist_process_naudio_cbfd(this);
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
WarnMessage("ABI3 identification regression: v=%08x", v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHle::normal_task_dispatching(void)
|
|
|
|
|
{
|
|
|
|
|
const unsigned int sum =
|
|
|
|
|
sum_bytes((const uint8_t *)dram_u32(this, *dmem_u32(this, TASK_UCODE)), min(*dmem_u32(this, TASK_UCODE_SIZE), 0xf80) >> 1);
|
|
|
|
|
|
|
|
|
|
switch (sum) {
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4]
|
2016-04-28 07:09:44 +00:00
|
|
|
|
case 0x278:
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// Nothing to emulate
|
2016-04-28 07:09:44 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// GFX: Twintris [misleading task->type == 0]
|
2016-04-28 07:09:44 +00:00
|
|
|
|
case 0x212ee:
|
|
|
|
|
if (m_ForwardGFX)
|
|
|
|
|
{
|
|
|
|
|
m_ProcessDList();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// JPEG: found in Pok<6F>mon Stadium J
|
2016-04-28 07:09:44 +00:00
|
|
|
|
case 0x2c85a:
|
|
|
|
|
jpeg_decode_PS0(this);
|
|
|
|
|
return;
|
|
|
|
|
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// JPEG: found in Zelda Ocarina of Time, Pok<6F>mon Stadium 1, Pok<6F>mon Stadium 2
|
2016-04-28 07:09:44 +00:00
|
|
|
|
case 0x2caa6:
|
|
|
|
|
jpeg_decode_PS(this);
|
|
|
|
|
return;
|
|
|
|
|
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// JPEG: found in Ogre Battle, Bottom of the 9th
|
2016-04-28 07:09:44 +00:00
|
|
|
|
case 0x130de:
|
|
|
|
|
case 0x278b0:
|
|
|
|
|
jpeg_decode_OB(this);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-18 11:51:36 +00:00
|
|
|
|
WarnMessage("Unknown OSTask: sum: %x PC:%x", sum, *m_sp_pc);
|
2016-04-28 07:09:44 +00:00
|
|
|
|
#ifdef ENABLE_TASK_DUMP
|
|
|
|
|
dump_unknown_task(this, sum);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHle::non_task_dispatching(void)
|
|
|
|
|
{
|
|
|
|
|
const unsigned int sum = sum_bytes(m_imem, 44);
|
|
|
|
|
|
|
|
|
|
if (sum == 0x9e2)
|
|
|
|
|
{
|
2021-03-16 05:29:28 +00:00
|
|
|
|
// CIC x105 microcode (used during boot of CIC x105 games)
|
2016-04-28 07:09:44 +00:00
|
|
|
|
cicx105_ucode(this);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-18 11:51:36 +00:00
|
|
|
|
WarnMessage("Unknown RSP code: sum: %x PC:%x", sum, *m_sp_pc);
|
2016-04-28 07:09:44 +00:00
|
|
|
|
#ifdef ENABLE_TASK_DUMP
|
|
|
|
|
dump_unknown_non_task(hle, sum);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(_WIN32) && defined(_DEBUG)
|
|
|
|
|
#include <Windows.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void CHle::VerboseMessage(const char *message, ...)
|
|
|
|
|
{
|
|
|
|
|
#if defined(_WIN32) && defined(_DEBUG)
|
2021-05-18 11:51:36 +00:00
|
|
|
|
// These can get annoying
|
2016-04-28 07:09:44 +00:00
|
|
|
|
#if 0
|
2021-03-16 05:29:28 +00:00
|
|
|
|
MessageBox(NULL, message, "HLE verbose message", MB_OK);
|
2016-04-28 07:09:44 +00:00
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHle::WarnMessage(const char *message, ...)
|
|
|
|
|
{
|
|
|
|
|
#if defined(_WIN32) && defined(_DEBUG)
|
2021-03-16 05:29:28 +00:00
|
|
|
|
MessageBoxA(NULL, message, "HLE warning message", MB_OK);
|
2016-04-28 07:09:44 +00:00
|
|
|
|
#endif
|
2021-03-16 05:29:28 +00:00
|
|
|
|
}
|