mirror of
https://github.com/mupen64plus-ae/mupen64plus-rsp-cxd4.git
synced 2024-11-23 13:40:01 +00:00
327 lines
10 KiB
C
327 lines
10 KiB
C
#ifndef _RSP_H_
|
|
#define _RSP_H_
|
|
|
|
#include "Rsp_#1.1.h"
|
|
RSP_INFO RSP;
|
|
|
|
#ifdef _MSC_VER
|
|
#define INLINE __inline
|
|
#define NOINLINE __declspec(noinline)
|
|
#define ALIGNED _declspec(align(16))
|
|
#else
|
|
#define INLINE inline
|
|
#define NOINLINE __attribute__((noinline))
|
|
#define ALIGNED __attribute__((aligned(16)))
|
|
#endif
|
|
|
|
/*
|
|
* Streaming SIMD Extensions version import management
|
|
*/
|
|
#ifdef ARCH_MIN_SSSE3
|
|
#define ARCH_MIN_SSE2
|
|
#include <tmmintrin.h>
|
|
#endif
|
|
#ifdef ARCH_MIN_SSE2
|
|
#include <emmintrin.h>
|
|
#endif
|
|
|
|
typedef unsigned char byte;
|
|
|
|
NOINLINE void message(const char* body, int priority)
|
|
{ /* Avoid SHELL32/ADVAPI32/USER32 dependencies by using standard C to print. */
|
|
char argv[4096] = "CMD /Q /D /C \"TITLE RSP Message&&ECHO ";
|
|
int i = 0;
|
|
int j = strlen(argv);
|
|
|
|
priority &= 03;
|
|
if (priority < MINIMUM_MESSAGE_PRIORITY)
|
|
return;
|
|
|
|
/*
|
|
* I'm just using system() to call the Windows command shell to print text.
|
|
* When the subsystem permits, use printf to trace messages, not this crap.
|
|
* I don't use WIN32 MessageBox because that's just extra OS dependencies. :P
|
|
*/
|
|
while (body[i] != '\0')
|
|
{
|
|
if (body[i] == '\n')
|
|
{
|
|
argv[j + 0] = '&';
|
|
argv[j + 1] = '&';
|
|
argv[j + 2] = 'E';
|
|
argv[j + 3] = 'C';
|
|
argv[j + 4] = 'H';
|
|
argv[j + 5] = 'O';
|
|
argv[j + 6] = ' ';
|
|
++i;
|
|
j += 7;
|
|
continue;
|
|
}
|
|
argv[j++] = body[i++];
|
|
}
|
|
strcat(argv, "&&PAUSE&&EXIT\"");
|
|
system(argv);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Update RSP configuration memory from local file resource.
|
|
*/
|
|
#define CHARACTERS_PER_LINE (80)
|
|
/* typical standard DOS text file limit per line */
|
|
NOINLINE void update_conf(const char* source)
|
|
{
|
|
FILE* stream;
|
|
char line[CHARACTERS_PER_LINE] = "";
|
|
char key[CHARACTERS_PER_LINE], value[CHARACTERS_PER_LINE];
|
|
register int i, test;
|
|
|
|
stream = fopen(source, "r");
|
|
if (stream == NULL)
|
|
{ /* try GetModulePath or whatever to correct the path? */
|
|
message("Failed to read config.", 3);
|
|
return;
|
|
}
|
|
do
|
|
{
|
|
int bvalue;
|
|
|
|
line[0] = '\0';
|
|
key[0] = '\0';
|
|
value[0] = '\0';
|
|
for (i = 0; i < CHARACTERS_PER_LINE; i++)
|
|
{
|
|
test = fgetc(stream);
|
|
if (test < 0) /* either EOF or invalid ASCII characters */
|
|
break;
|
|
line[i] = (char)(test);
|
|
if (line[i] == '\n')
|
|
break;
|
|
}
|
|
line[i] = '\0';
|
|
|
|
for (i = 0; i < CHARACTERS_PER_LINE && line[i] != '='; i++);
|
|
line[i] = '\0';
|
|
strcpy(key, line);
|
|
strcpy(value, line + i + 1);
|
|
|
|
bvalue = atoi(value);
|
|
if (strcmp(key, "DisplayListToGraphicsPlugin") == 0)
|
|
CFG_HLE_GFX = (byte)(bvalue);
|
|
else if (strcmp(key, "AudioListToAudioPlugin") == 0)
|
|
CFG_HLE_AUD = (byte)(bvalue);
|
|
else if (strcmp(key, "WaitForCPUHost") == 0)
|
|
CFG_WAIT_FOR_CPU_HOST = bvalue;
|
|
else if (strcmp(key, "SupportCPUSemaphoreLock") == 0)
|
|
CFG_MEND_SEMAPHORE_LOCK = bvalue;
|
|
} while (test != EOF);
|
|
fclose(stream);
|
|
return;
|
|
}
|
|
|
|
#ifndef EMULATE_STATIC_PC
|
|
static int stage;
|
|
#endif
|
|
static int temp_PC;
|
|
#ifdef WAIT_FOR_CPU_HOST
|
|
static short MFC0_count[32];
|
|
/* Keep one C0 MF status read count for each scalar register. */
|
|
#endif
|
|
|
|
#ifdef SP_EXECUTE_LOG
|
|
static FILE *output_log;
|
|
extern void step_SP_commands(unsigned long inst);
|
|
#endif
|
|
extern void export_SP_memory(void);
|
|
NOINLINE void trace_RSP_registers(void);
|
|
|
|
#include "su.h"
|
|
#include "vu/vu.h"
|
|
|
|
/* Allocate the RSP CPU loop to its own functional space. */
|
|
NOINLINE extern void run_task(void);
|
|
#include "execute.h"
|
|
|
|
#ifdef SP_EXECUTE_LOG
|
|
void step_SP_commands(unsigned long inst)
|
|
{
|
|
if (output_log)
|
|
{
|
|
const char digits[16] = {
|
|
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
|
|
};
|
|
char text[256];
|
|
char offset[4] = "";
|
|
char code[9] = "";
|
|
unsigned char endian_swap[4];
|
|
|
|
endian_swap[00] = (unsigned char)(inst >> 24);
|
|
endian_swap[01] = (unsigned char)(inst >> 16);
|
|
endian_swap[02] = (unsigned char)(inst >> 8);
|
|
endian_swap[03] = (unsigned char)inst;
|
|
offset[00] = digits[(*RSP.SP_PC_REG & 0xF00) >> 8];
|
|
offset[01] = digits[(*RSP.SP_PC_REG & 0x0F0) >> 4];
|
|
offset[02] = digits[(*RSP.SP_PC_REG & 0x00F) >> 0];
|
|
code[00] = digits[(inst & 0xF0000000) >> 28];
|
|
code[01] = digits[(inst & 0x0F000000) >> 24];
|
|
code[02] = digits[(inst & 0x00F00000) >> 20];
|
|
code[03] = digits[(inst & 0x000F0000) >> 16];
|
|
code[04] = digits[(inst & 0x0000F000) >> 12];
|
|
code[05] = digits[(inst & 0x00000F00) >> 8];
|
|
code[06] = digits[(inst & 0x000000F0) >> 4];
|
|
code[07] = digits[(inst & 0x0000000F) >> 0];
|
|
strcpy(text, offset);
|
|
strcat(text, "\n");
|
|
strcat(text, code);
|
|
message(text, 0); /* PC offset, MIPS hex. */
|
|
if (output_log == NULL) {} else /* Global pointer not updated?? */
|
|
fwrite(endian_swap, 4, 1, output_log);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
NOINLINE void export_data_cache(void)
|
|
{
|
|
FILE* out;
|
|
register unsigned long addr;
|
|
|
|
out = fopen("rcpcache.dhex", "wb");
|
|
#if (0)
|
|
for (addr = 0x00000000; addr < 0x00001000; addr += 0x00000001)
|
|
fputc(RSP.DMEM[BES(addr & 0x00000FFF)], out);
|
|
#else
|
|
for (addr = 0x00000000; addr < 0x00001000; addr += 0x00000004)
|
|
{
|
|
fputc(RSP.DMEM[addr + 0x000 + BES(0x000)], out);
|
|
fputc(RSP.DMEM[addr + 0x001 + MES(0x000)], out);
|
|
fputc(RSP.DMEM[addr + 0x002 - MES(0x000)], out);
|
|
fputc(RSP.DMEM[addr + 0x003 - BES(0x000)], out);
|
|
}
|
|
#endif
|
|
fclose(out);
|
|
return;
|
|
}
|
|
NOINLINE void export_instruction_cache(void)
|
|
{
|
|
FILE* out;
|
|
register unsigned long addr;
|
|
|
|
out = fopen("rcpcache.ihex", "wb");
|
|
#if (0)
|
|
for (addr = 0x00000000; addr < 0x00001000; addr += 0x00000001)
|
|
fputc(RSP.IMEM[BES(addr & 0x00000FFF)], out);
|
|
#else
|
|
for (addr = 0x00000000; addr < 0x00001000; addr += 0x00000004)
|
|
{
|
|
fputc(RSP.IMEM[addr + 0x000 + BES(0x000)], out);
|
|
fputc(RSP.IMEM[addr + 0x001 + MES(0x000)], out);
|
|
fputc(RSP.IMEM[addr + 0x002 - MES(0x000)], out);
|
|
fputc(RSP.IMEM[addr + 0x003 - BES(0x000)], out);
|
|
}
|
|
#endif
|
|
fclose(out);
|
|
return;
|
|
}
|
|
void export_SP_memory(void)
|
|
{
|
|
export_data_cache();
|
|
export_instruction_cache();
|
|
return;
|
|
}
|
|
|
|
const char CR_names[16][14] = {
|
|
"SP_MEM_ADDR ","SP_DRAM_ADDR ","SP_DMA_RD_LEN","SP_DMA_WR_LEN",
|
|
"SP_STATUS ","SP_DMA_FULL ","SP_DMA_BUSY ","SP_SEMAPHORE ",
|
|
"CMD_START ","CMD_END ","CMD_CURRENT ","CMD_STATUS ",
|
|
"CMD_CLOCK ","CMD_BUSY ","CMD_PIPE_BUSY","CMD_TMEM_BUSY",
|
|
};
|
|
const char SR_names[32][5] = {
|
|
"zero","$at:"," v0:"," v1:"," a0:"," a1:"," a2:"," a3:",
|
|
" t0:"," t1:"," t2:"," t3:"," t4:"," t5:"," t6:"," t7:",
|
|
" s0:"," s1:"," s2:"," s3:"," s4:"," s5:"," s6:"," s7:",
|
|
" t8:"," t9:"," k0:"," k1:"," gp:","$sp:","$s8:","$ra:",
|
|
};
|
|
|
|
NOINLINE void trace_RSP_registers(void)
|
|
{
|
|
register int i;
|
|
FILE* out;
|
|
|
|
out = fopen("SP_STATE.TXT", "w");
|
|
fprintf(out, "RCP Communications Register Display\n\n");
|
|
fclose(out);
|
|
|
|
out = fopen("SP_STATE.TXT", "a");
|
|
/*
|
|
* The precise names for RSP CP0 registers are somewhat difficult to define.
|
|
* Generally, I have tried to adhere to the names shared from bpoint/zilmar,
|
|
* while also merging the concrete purpose and correct assembler references.
|
|
* Whether or not you find these names agreeable is mostly a matter of seeing
|
|
* them from the RCP's point of view or the CPU host's mapped point of view.
|
|
*/
|
|
for (i = 0; i < 8; i++)
|
|
fprintf(out, "%s: %08lX %s: %08lX\n",
|
|
CR_names[i+0], *(CR[i+0]), CR_names[i+8], *(CR[i+8]));
|
|
fprintf(out, "SP_PC_REG: %08lX\n\n", *RSP.SP_PC_REG);
|
|
/* (PC is only from the CPU point of view, mapped between both halves.) */
|
|
/*
|
|
* There is no memory map for remaining registers not shared by the CPU.
|
|
* The scalar register (SR) file is straightforward and based on the
|
|
* GPR file in the MIPS ISA. However, the RSP assembly language is still
|
|
* different enough from the MIPS assembly language, in that tokens such as
|
|
* "$zero" and "$s0" are no longer valid. "$k0", for example, is not a valid
|
|
* RSP register name because on MIPS it was kernel-use, but on the RSP, free.
|
|
* To be colorful/readable, however, I have set the modern MIPS names anyway.
|
|
*/
|
|
for (i = 0; i < 16; i++)
|
|
fprintf(out, "%s %08X, %s %08X,\n",
|
|
SR_names[i+0], SR[i+0], SR_names[i+16], SR[i+16]);
|
|
fprintf(out, "\n");
|
|
|
|
for (i = 0; i < 10; i++)
|
|
fprintf(
|
|
out,
|
|
" $v%i: [%04hX][%04hX][%04hX][%04hX][%04hX][%04hX][%04hX][%04hX]\n",
|
|
i,
|
|
VR[i][00], VR[i][01], VR[i][02], VR[i][03],
|
|
VR[i][04], VR[i][05], VR[i][06], VR[i][07]);
|
|
for (i = 10; i < 32; i++) /* decimals "10" and higher with two characters */
|
|
fprintf(
|
|
out,
|
|
"$v%i: [%04hX][%04hX][%04hX][%04hX][%04hX][%04hX][%04hX][%04hX]\n",
|
|
i,
|
|
VR[i][00], VR[i][01], VR[i][02], VR[i][03],
|
|
VR[i][04], VR[i][05], VR[i][06], VR[i][07]);
|
|
fprintf(out, "\n");
|
|
|
|
/*
|
|
* The SU has its fair share of registers, but the VU has its counterparts.
|
|
* Just like we have the scalar 16 system control registers for the RSP CP0,
|
|
* we have also a tiny group of special-purpose, vector control registers.
|
|
*/
|
|
fprintf(out, "$vco: 0x%04X\n", get_VCO());
|
|
fprintf(out, "$vcc: 0x%04X\n", get_VCC());
|
|
fprintf(out, "$vce: 0x%02X\n\n", get_VCE());
|
|
|
|
/*
|
|
* 48-bit RSP accumulators
|
|
* Most vector unit patents traditionally call this register file "VACC".
|
|
* However, typically in discussion about SGI's implementation, we say "ACC".
|
|
*/
|
|
for (i = 0; i < 8; i++)
|
|
fprintf(
|
|
out, "ACC[%o]: [%04X][%04X][%04X]\n", i,
|
|
VACC_H[i], VACC_M[i], VACC_L[i]);
|
|
|
|
/*
|
|
* special-purpose vector unit registers for vector divide operations only
|
|
*/
|
|
fprintf(out, "\nDivIn: %i\n", DivIn);
|
|
fprintf(out, "DivOut: %i\n", DivOut);
|
|
/* MovIn: This reg. might exist for VMOV, but it is obsolete to emulate. */
|
|
fprintf(out, DPH ? "DPH: true\n" : "DPH: false\n");
|
|
fclose(out);
|
|
return;
|
|
}
|
|
#endif
|