#include <stdbool.h>
#include <stdint.h>
#ifdef ENABLE_TASK_DUMP
#include <stdio.h>
#endif
#include "common.h"
#include "hle_external.h"
#include "hle_internal.h"
#include "memory.h"
#include "ucodes.h"
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define SP_STATUS_HALT 0x1
#define SP_STATUS_BROKE 0x2
#define SP_STATUS_INTR_ON_BREAK 0x40
#define SP_STATUS_TASKDONE 0x200
#define DP_STATUS_FREEZE 0x2
#define MI_INTR_SP 0x1
static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size);
static bool is_task(struct hle_t* hle);
static void rsp_break(struct hle_t* hle, unsigned int setbits);
static void forward_gfx_task(struct hle_t* hle);
static bool try_fast_audio_dispatching(struct hle_t* hle);
static bool try_fast_task_dispatching(struct hle_t* hle);
static void normal_task_dispatching(struct hle_t* hle);
static void non_task_dispatching(struct hle_t* hle);
#ifdef ENABLE_TASK_DUMP
static void dump_binary(const char *const filename, const unsigned char *const bytes,
unsigned int size);
static void dump_task(struct hle_t* hle, const char *const filename);
static void dump_unknown_task(struct hle_t* hle, unsigned int sum);
static void dump_unknown_non_task(struct hle_t* hle, unsigned int sum);
#endif
static const bool FORWARD_AUDIO = false, FORWARD_GFX = true;
void hle_init(struct hle_t* hle,
unsigned char* dram,
unsigned char* dmem,
unsigned char* imem,
unsigned int* mi_intr,
unsigned int* sp_mem_addr,
unsigned int* sp_dram_addr,
unsigned int* sp_rd_length,
unsigned int* sp_wr_length,
unsigned int* sp_status,
unsigned int* sp_dma_full,
unsigned int* sp_dma_busy,
unsigned int* sp_pc,
unsigned int* sp_semaphore,
unsigned int* dpc_start,
unsigned int* dpc_end,
unsigned int* dpc_current,
unsigned int* dpc_status,
unsigned int* dpc_clock,
unsigned int* dpc_bufbusy,
unsigned int* dpc_pipebusy,
unsigned int* dpc_tmem,
void* user_defined)
{
hle->dram = dram;
hle->dmem = dmem;
hle->imem = imem;
hle->mi_intr = mi_intr;
hle->sp_mem_addr = sp_mem_addr;
hle->sp_dram_addr = sp_dram_addr;
hle->sp_rd_length = sp_rd_length;
hle->sp_wr_length = sp_wr_length;
hle->sp_status = sp_status;
hle->sp_dma_full = sp_dma_full;
hle->sp_dma_busy = sp_dma_busy;
hle->sp_pc = sp_pc;
hle->sp_semaphore = sp_semaphore;
hle->dpc_start = dpc_start;
hle->dpc_end = dpc_end;
hle->dpc_current = dpc_current;
hle->dpc_status = dpc_status;
hle->dpc_clock = dpc_clock;
hle->dpc_bufbusy = dpc_bufbusy;
hle->dpc_pipebusy = dpc_pipebusy;
hle->dpc_tmem = dpc_tmem;
hle->user_defined = user_defined;
}
void hle_execute(struct hle_t* hle)
{
if (is_task(hle)) {
if (!try_fast_task_dispatching(hle))
normal_task_dispatching(hle);
rsp_break(hle, SP_STATUS_TASKDONE);
} else {
non_task_dispatching(hle);
rsp_break(hle, 0);
}
}
static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size)
{
unsigned int sum = 0;
const unsigned char *const bytes_end = bytes + size;
while (bytes != bytes_end)
sum += *bytes++;
return sum;
}
static bool is_task(struct hle_t* hle)
{
return (*dmem_u32(hle, TASK_UCODE_BOOT_SIZE) <= 0x1000);
}
static void rsp_break(struct hle_t* hle, unsigned int setbits)
{
*hle->sp_status |= setbits | SP_STATUS_BROKE | SP_STATUS_HALT;
if ((*hle->sp_status & SP_STATUS_INTR_ON_BREAK)) {
*hle->mi_intr |= MI_INTR_SP;
HleCheckInterrupts(hle->user_defined);
}
}
static void forward_gfx_task(struct hle_t* hle)
{
HleProcessDlistList(hle->user_defined);
*hle->dpc_status &= ~DP_STATUS_FREEZE;
}
static bool try_fast_audio_dispatching(struct hle_t* hle)
{
uint32_t ucode_data = *dmem_u32(hle, TASK_UCODE_DATA);
uint32_t v;
if (*dram_u32(hle, ucode_data) == 0x00000001) {
if (*dram_u32(hle, ucode_data + 0x30) == 0xf0000f00) {
v = *dram_u32(hle, ucode_data + 0x28);
switch(v)
{
case 0x1e24138c:
alist_process_audio(hle); return true;
case 0x1dc8138c:
alist_process_audio_ge(hle); return true;
case 0x1e3c1390:
alist_process_audio_bc(hle); return true;
default:
HleWarnMessage(hle->user_defined, "ABI1 identification regression: v=%08x", v);
}
} else {
v = *dram_u32(hle, ucode_data + 0x10);
switch(v)
{
case 0x11181350:
alist_process_nead_mk(hle); return true;
case 0x111812e0:
alist_process_nead_sfj(hle); return true;
case 0x110412ac:
alist_process_nead_wrjb(hle); return true;
case 0x110412cc:
alist_process_nead_sf(hle); return true;
case 0x1cd01250:
alist_process_nead_fz(hle); return true;
case 0x1f08122c:
alist_process_nead_ys(hle); return true;
case 0x1f38122c:
alist_process_nead_1080(hle); return true;
case 0x1f681230:
alist_process_nead_oot(hle); return true;
case 0x1f801250:
alist_process_nead_mm(hle); return true;
case 0x109411f8:
alist_process_nead_mmb(hle); return true;
case 0x1eac11b8:
alist_process_nead_ac(hle); return true;
case 0x00010010:
musyx_v2_task(hle); return true;
default:
HleWarnMessage(hle->user_defined, "ABI2 identification regression: v=%08x", v);
}
}
} else {
v = *dram_u32(hle, ucode_data + 0x10);
switch(v)
{
case 0x00000001:
musyx_v1_task(hle); return true;
case 0x0000127c:
alist_process_naudio(hle); return true;
case 0x00001280:
alist_process_naudio_bk(hle); return true;
case 0x1c58126c:
alist_process_naudio_dk(hle); return true;
case 0x1ae8143c:
alist_process_naudio_mp3(hle); return true;
case 0x1ab0140c:
alist_process_naudio_cbfd(hle); return true;
default:
HleWarnMessage(hle->user_defined, "ABI3 identification regression: v=%08x", v);
}
}
return false;
}
static bool try_fast_task_dispatching(struct hle_t* hle)
{
switch (*dmem_u32(hle, TASK_TYPE)) {
case 1:
if (FORWARD_GFX) {
forward_gfx_task(hle);
return true;
}
break;
case 2:
if (FORWARD_AUDIO) {
HleProcessAlistList(hle->user_defined);
return true;
} else if (try_fast_audio_dispatching(hle))
return true;
break;
case 7:
HleShowCFB(hle->user_defined);
return true;
}
return false;
}
static void normal_task_dispatching(struct hle_t* hle)
{
const unsigned int sum =
sum_bytes((void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE)), min(*dmem_u32(hle, TASK_UCODE_SIZE), 0xf80) >> 1);
switch (sum) {
case 0x278:
return;
case 0x212ee:
if (FORWARD_GFX) {
forward_gfx_task(hle);
return;
}
break;
case 0x2c85a:
jpeg_decode_PS0(hle);
return;
case 0x2caa6:
jpeg_decode_PS(hle);
return;
case 0x130de:
case 0x278b0:
jpeg_decode_OB(hle);
return;
}
HleWarnMessage(hle->user_defined, "unknown OSTask: sum: %x PC:%x", sum, *hle->sp_pc);
#ifdef ENABLE_TASK_DUMP
dump_unknown_task(hle, sum);
#endif
}
static void non_task_dispatching(struct hle_t* hle)
{
const unsigned int sum = sum_bytes(hle->imem, 0x1000 >> 1);
switch (sum) {
case 0x9e2:
case 0x9f2:
cicx105_ucode(hle);
return;
}
HleWarnMessage(hle->user_defined, "unknown RSP code: sum: %x PC:%x", sum, *hle->sp_pc);
#ifdef ENABLE_TASK_DUMP
dump_unknown_non_task(hle, sum);
#endif
}
#ifdef ENABLE_TASK_DUMP
static void dump_unknown_task(struct hle_t* hle, unsigned int sum)
{
char filename[256];
uint32_t ucode = *dmem_u32(hle, TASK_UCODE);
uint32_t ucode_data = *dmem_u32(hle, TASK_UCODE_DATA);
uint32_t data_ptr = *dmem_u32(hle, TASK_DATA_PTR);
sprintf(&filename[0], "task_%x.log", sum);
dump_task(hle, filename);
sprintf(&filename[0], "ucode_boot_%x.bin", sum);
dump_binary(filename, (void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE_BOOT)), *dmem_u32(hle, TASK_UCODE_BOOT_SIZE));
if (ucode != 0) {
sprintf(&filename[0], "ucode_%x.bin", sum);
dump_binary(filename, (void*)dram_u32(hle, ucode), 0xf80);
}
if (ucode_data != 0) {
sprintf(&filename[0], "ucode_data_%x.bin", sum);
dump_binary(filename, (void*)dram_u32(hle, ucode_data), *dmem_u32(hle, TASK_UCODE_DATA_SIZE));
}
if (data_ptr != 0) {
sprintf(&filename[0], "data_%x.bin", sum);
dump_binary(filename, (void*)dram_u32(hle, data_ptr), *dmem_u32(hle, TASK_DATA_SIZE));
}
}
static void dump_unknown_non_task(struct hle_t* hle, unsigned int sum)
{
char filename[256];
sprintf(&filename[0], "imem_%x.bin", sum);
dump_binary(filename, hle->imem, 0x1000);
sprintf(&filename[0], "dmem_%x.bin", sum);
dump_binary(filename, hle->dmem, 0x1000);
}
static void dump_binary(const char *const filename, const unsigned char *const bytes,
unsigned int size)
{
FILE *f;
f = fopen(filename, "r");
if (f == NULL) {
f = fopen(filename, "wb");
if (f != NULL) {
if (fwrite(bytes, 1, size, f) != size)
hleErrorMessage(hle->user_defined, "Writing error on %s", filename);
fclose(f);
} else
hleErrorMessage(hle->user_defined, "Couldn't open %s for writing !", filename);
} else
fclose(f);
}
static void dump_task(struct hle_t* hle, const char *const filename)
{
FILE *f;
f = fopen(filename, "r");
if (f == NULL) {
f = fopen(filename, "w");
fprintf(f,
"type = %d\n"
"flags = %d\n"
"ucode_boot = %#08x size = %#x\n"
"ucode = %#08x size = %#x\n"
"ucode_data = %#08x size = %#x\n"
"dram_stack = %#08x size = %#x\n"
"output_buff = %#08x *size = %#x\n"
"data = %#08x size = %#x\n"
"yield_data = %#08x size = %#x\n",
*dmem_u32(hle, TASK_TYPE),
*dmem_u32(hle, TASK_FLAGS),
*dmem_u32(hle, TASK_UCODE_BOOT), *dmem_u32(hle, TASK_UCODE_BOOT_SIZE),
*dmem_u32(hle, TASK_UCODE), *dmem_u32(hle, TASK_UCODE_SIZE),
*dmem_u32(hle, TASK_UCODE_DATA), *dmem_u32(hle, TASK_UCODE_DATA_SIZE),
*dmem_u32(hle, TASK_DRAM_STACK), *dmem_u32(hle, TASK_DRAM_STACK_SIZE),
*dmem_u32(hle, TASK_OUTPUT_BUFF), *dmem_u32(hle, TASK_OUTPUT_BUFF_SIZE),
*dmem_u32(hle, TASK_DATA_PTR), *dmem_u32(hle, TASK_DATA_SIZE),
*dmem_u32(hle, TASK_YIELD_DATA_PTR), *dmem_u32(hle, TASK_YIELD_DATA_SIZE));
fclose(f);
} else
fclose(f);
}
#endif