mirror of
https://github.com/mupen64plus-ae/mupen64plus-rsp-cxd4.git
synced 2024-11-26 23:10:25 +00:00
263 lines
9.3 KiB
C
263 lines
9.3 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
|
|
|
|
NOINLINE void message(const char* body, int priority)
|
|
{ /* Avoid SHELL32/ADVAPI32/USER32 dependencies by using standard C to print. */
|
|
char argv[4096] = "CMD /D /S /Q /T:0E /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 OS permits it, just 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++] = '&';
|
|
argv[j++] = '&';
|
|
argv[j++] = 'E';
|
|
argv[j++] = 'C';
|
|
argv[j++] = 'H';
|
|
argv[j++] = 'O';
|
|
argv[j++] = ' ';
|
|
++i;
|
|
continue;
|
|
}
|
|
argv[j++] = body[i++];
|
|
}
|
|
strcat(argv, " && PAUSE\"");
|
|
system(argv);
|
|
}
|
|
|
|
/*
|
|
* Update RSP configuration memory from local file resource.
|
|
*/
|
|
void update_conf(const char* source)
|
|
{
|
|
FILE* stream;
|
|
unsigned char checksum;
|
|
register int i, test;
|
|
|
|
stream = fopen(source, "rb");
|
|
if (stream == NULL)
|
|
{ /* try GetModulePath or whatever to correct the path? */
|
|
message("Failed to read config.", 3);
|
|
return;
|
|
}
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
test = fgetc(stream);
|
|
conf[i] = (unsigned char)(test);
|
|
}
|
|
/* my own little checksum code, not really useful :P */
|
|
checksum = 0x00000000;
|
|
for (i = 0; i < 31; i++)
|
|
checksum = checksum
|
|
+ !!(conf[i] & 0x80)
|
|
+ !!(conf[i] & 0x40)
|
|
+ !!(conf[i] & 0x20)
|
|
+ !!(conf[i] & 0x10)
|
|
+ !!(conf[i] & 0x08)
|
|
+ !!(conf[i] & 0x04)
|
|
+ !!(conf[i] & 0x02)
|
|
+ !!(conf[i] & 0x01);
|
|
if (checksum != CFG_CHECKSUM)
|
|
{
|
|
message("Checksum mismatch.", 3);
|
|
memset(conf, 0x00, 32);
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static int stage;
|
|
#ifdef WAIT_FOR_CPU_HOST
|
|
static int MFC0_count[32];
|
|
/* Keep one C0 MF status read count for each scalar register. */
|
|
#endif
|
|
|
|
#include "su.h"
|
|
#include "vu/vu.h"
|
|
|
|
extern void step_SP_commands(unsigned long inst);
|
|
extern void export_SP_memory(void);
|
|
extern void trace_RSP_registers(void);
|
|
static FILE *output_log;
|
|
|
|
/* Allocate the RSP CPU loop to its own functional space. */
|
|
extern void run_task(void);
|
|
#include "execute.h"
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void export_SP_memory(void)
|
|
{ /* cache memory and dynamic RAM shared by CPU */
|
|
FILE *out = fopen("SP_CACHE.BIN", "wb");
|
|
fwrite(RSP.DMEM, sizeof(unsigned char), 0x1FFF + 1, out);
|
|
fclose(out);
|
|
out = fopen("SP_DRAM.BIN", "wb");
|
|
fwrite(RSP.RDRAM, sizeof(unsigned char), 0x3FFFFF + 1, out);
|
|
return;
|
|
}
|
|
|
|
void trace_RSP_registers(void)
|
|
{ /* no interface--using file I/O only */
|
|
register int i;
|
|
FILE *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.
|
|
*/
|
|
fprintf(out, "SP_MEM_ADDR: %08lX CMD_START: %08lX\n",
|
|
*RSP.SP_MEM_ADDR_REG, *RSP.DPC_START_REG);
|
|
fprintf(out, "SP_DRAM_ADDR: %08lX CMD_END: %08lX\n",
|
|
*RSP.SP_DRAM_ADDR_REG, *RSP.DPC_END_REG);
|
|
fprintf(out, "SP_DMA_RD_LEN: %08lX CMD_CURRENT: %08lX\n",
|
|
*RSP.SP_RD_LEN_REG, *RSP.DPC_CURRENT_REG);
|
|
fprintf(out, "SP_DMA_WR_LEN: %08lX CMD_STATUS: %08lX\n",
|
|
*RSP.SP_WR_LEN_REG, *RSP.DPC_STATUS_REG);
|
|
fprintf(out, "SP_STATUS: %08lX CMD_CLOCK: %08lX\n",
|
|
*RSP.SP_STATUS_REG, *RSP.DPC_CLOCK_REG);
|
|
fprintf(out, "SP_DMA_FULL: %08lX CMD_BUSY: %08lX\n",
|
|
*RSP.SP_DMA_FULL_REG, *RSP.DPC_BUFBUSY_REG);
|
|
fprintf(out, "SP_DMA_BUSY: %08lX CMD_PIPE_BUSY: %08lX\n",
|
|
*RSP.SP_DMA_BUSY_REG, *RSP.DPC_PIPEBUSY_REG);
|
|
fprintf(out, "SP_SEMAPHORE: %08lX CMD_TMEM_BUSY: %08lX\n",
|
|
*RSP.SP_SEMAPHORE_REG, *RSP.DPC_TMEM_REG);
|
|
fprintf(out, "SP_PC_REG: 04001%03lX\n\n", *RSP.SP_PC_REG & 0x00000FFF);
|
|
/* (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.
|
|
*/
|
|
fprintf(out, "zero %08X, s0: %08X,\n", SR[ 0], SR[16]);
|
|
fprintf(out, "$at: %08X, s1: %08X,\n", SR[ 1], SR[17]);
|
|
fprintf(out, " v0: %08X, s2: %08X,\n", SR[ 2], SR[18]);
|
|
fprintf(out, " v1: %08X, s3: %08X,\n", SR[ 3], SR[19]);
|
|
fprintf(out, " a0: %08X, s4: %08X,\n", SR[ 4], SR[20]);
|
|
fprintf(out, " a1: %08X, s5: %08X,\n", SR[ 5], SR[21]);
|
|
fprintf(out, " a2: %08X, s6: %08X,\n", SR[ 6], SR[22]);
|
|
fprintf(out, " a3: %08X, s7: %08X,\n", SR[ 7], SR[23]);
|
|
fprintf(out, " t0: %08X, t8: %08X,\n", SR[ 8], SR[24]);
|
|
fprintf(out, " t1: %08X, t9: %08X,\n", SR[ 9], SR[25]);
|
|
fprintf(out, " t2: %08X, k0: %08X,\n", SR[10], SR[26]);
|
|
fprintf(out, " t3: %08X, k1: %08X,\n", SR[11], SR[27]);
|
|
fprintf(out, " t4: %08X, gp: %08X,\n", SR[12], SR[28]);
|
|
fprintf(out, " t5: %08X, $sp: %08X,\n", SR[13], SR[29]);
|
|
fprintf(out, " t6: %08X, $s8: %08X,\n", SR[14], SR[30]);
|
|
fprintf(out, " t7: %08X, $ra: %08X\n\n", SR[15], SR[31]);
|
|
|
|
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", get_VCE());
|
|
fprintf(out, "\n");
|
|
for (i = 0; i < 8; i++)
|
|
fprintf(
|
|
out, "ACC[%o]: [%04X][%04X][%04X]\n", i,
|
|
ACC_H(i), ACC_M(i), ACC_L(i));
|
|
fprintf(out, "\n");
|
|
fprintf(out, "DivIn: %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
|