#include <SDL.h> // Used only for SDL_GetTicks...
#include <stdlib.h>
-#include <time.h>
#include "dac.h"
#include "gpu.h"
#include "jagdasm.h"
static void dsp_opcode_subqt(void);
static void dsp_opcode_illegal(void);
-uint8_t dsp_opcode_cycles[64] =
+/*uint8_t dsp_opcode_cycles[64] =
{
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3,
//This is wrong, wrong, WRONG, but it seems to work for the time being...
//(That is, it fixes Flip Out which relies on GPU timing rather than semaphores. Bad developers! Bad!)
//What's needed here is a way to take pipeline effects into account (including pipeline stalls!)...
-/*uint8_t dsp_opcode_cycles[64] =
+// Yup, without cheating like this, the sound in things like Rayman, FACTS, &
+// Tripper Getem get starved for time and sounds like crap. So we have to figure
+// out how to fix that. :-/
+uint8_t dsp_opcode_cycles[64] =
{
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
dsp_opcode_use[i] = 0;
}
+
void DSPReleaseTimeslice(void)
{
//This does absolutely nothing!!! !!! FIX !!!
dsp_releaseTimeSlice_flag = 1;
}
+
void dsp_build_branch_condition_table(void)
{
// Fill in the mirror table
}
}
+
uint8_t DSPReadByte(uint32_t offset, uint32_t who/*=UNKNOWN*/)
{
if (offset >= 0xF1A000 && offset <= 0xF1A0FF)
{
uint32_t data = DSPReadLong(offset & 0xFFFFFFFC, who);
- if ((offset&0x03)==0)
- return(data>>24);
- else
- if ((offset&0x03)==1)
- return((data>>16)&0xff);
- else
- if ((offset&0x03)==2)
- return((data>>8)&0xff);
- else
- if ((offset&0x03)==3)
- return(data&0xff);
+ if ((offset & 0x03) == 0)
+ return (data >> 24);
+ else if ((offset & 0x03) == 1)
+ return ((data >> 16) & 0xFF);
+ else if ((offset & 0x03) == 2)
+ return ((data >> 8) & 0xFF);
+ else if ((offset & 0x03) == 3)
+ return (data & 0xFF);
}
return JaguarReadByte(offset, who);
}
+
uint16_t DSPReadWord(uint32_t offset, uint32_t who/*=UNKNOWN*/)
{
if (offset >= 0xF1A000 && offset <= 0xF1A0FF)
return JaguarReadWord(offset, who);
}
+
uint32_t DSPReadLong(uint32_t offset, uint32_t who/*=UNKNOWN*/)
{
if (offset >= 0xF1A000 && offset <= 0xF1A0FF)
return JaguarReadLong(offset, who);
}
+
void DSPWriteByte(uint32_t offset, uint8_t data, uint32_t who/*=UNKNOWN*/)
{
if (offset >= 0xF1A000 && offset <= 0xF1A0FF)
WriteLog("DSP: WriteByte--Attempt to write to DSP register file by %s!\n", whoName[who]);
- if ((offset >= DSP_WORK_RAM_BASE) && (offset < DSP_WORK_RAM_BASE+0x2000))
+ if ((offset >= DSP_WORK_RAM_BASE) && (offset < DSP_WORK_RAM_BASE + 0x2000))
{
offset -= DSP_WORK_RAM_BASE;
dsp_ram_8[offset] = data;
}*/
return;
}
- if ((offset >= DSP_CONTROL_RAM_BASE) && (offset < DSP_CONTROL_RAM_BASE+0x20))
+ if ((offset >= DSP_CONTROL_RAM_BASE) && (offset < DSP_CONTROL_RAM_BASE + 0x20))
{
uint32_t reg = offset & 0x1C;
int bytenum = offset & 0x03;
}
// WriteLog("dsp: writing %.2x at 0x%.8x\n",data,offset);
//Should this *ever* happen??? Shouldn't we be saying "unknown" here???
+// Well, yes, it can. There are 3 MMU users after all: 68K, GPU & DSP...!
JaguarWriteByte(offset, data, who);
}
+
void DSPWriteWord(uint32_t offset, uint16_t data, uint32_t who/*=UNKNOWN*/)
{
if (offset >= 0xF1A000 && offset <= 0xF1A0FF)
JaguarWriteWord(offset, data, who);
}
+
//bool badWrite = false;
void DSPWriteLong(uint32_t offset, uint32_t data, uint32_t who/*=UNKNOWN*/)
{
DSPUpdateRegisterBanks();
dsp_control &= ~((dsp_flags & CINT04FLAGS) >> 3);
dsp_control &= ~((dsp_flags & CINT5FLAG) >> 1);
-
-// NB: This is just a wild hairy-assed guess as to what the playback frequency is.
-// It can be timed to anything really, anything that writes to L/RTXD at a regular
-// interval. Most things seem to use either the I2S interrupt or the TIMER 0
-// interrupt, so that's what we check for here. Just know that this approach
-// can be easily fooled!
-// Note also that if both interrupts are enabled, the I2S freq will win. :-P
-
-// Further Note:
-// The impetus for this "fix" was Cybermorph, which sets the SCLK to 7 which is an
-// audio frequency > 48 KHz. However, it stuffs the L/RTXD registers using TIMER0.
-// So, while this works, it's a by-product of the lame way in which audio is currently
-// handled. Hopefully, once we run the DSP in the host audio IRQ, this problem will
-// go away of its own accord. :-P
-// Or does it? It seems the I2S interrupt isn't on with Cybermorph, so something
-// weird is going on here...
-// Maybe it works like this: It acknowledges the 1st interrupt, but never clears it.
-// So subsequent interrupts come into the chip, but they're never serviced but the
-// I2S subsystem keeps going.
-// After some testing on real hardware, it seems that if you enable TIMER0 and EXTERNAL
-// IRQs on J_INT ($F10020), you don't have to run an I2S interrupt on the DSP. Also,
-// It seems that it's only stable for values of SCLK <= 9.
-
-// All of the preceeding is moot now; we run the DSP in the host audio IRQ. This means
-// that we don't actually need this stuff anymore. :-D
-#if 0
- if (data & INT_ENA1) // I2S interrupt
- {
- int freq = GetCalculatedFrequency();
-//This happens too often to be useful...
-// WriteLog("DSP: Setting audio freqency to %u Hz...\n", freq);
- DACSetNewFrequency(freq);
- }
- else if (data & INT_ENA2) // TIMER 0 interrupt
- {
- int freq = JERRYGetPIT1Frequency();
-//This happens too often to be useful...
-// WriteLog("DSP: Setting audio freqency to %u Hz...\n", freq);
- DACSetNewFrequency(freq);
- }
-#endif
-
-/* if (IMASKCleared) // If IMASK was cleared,
-#ifdef DSP_DEBUG_IRQ
- {
- WriteLog("DSP: Finished interrupt.\n");
-#endif
- DSPHandleIRQs(); // see if any other interrupts need servicing!
-#ifdef DSP_DEBUG_IRQ
- }
-#endif//*/
-#if 0
- if (/*4-8, 16*/data & 0x101F0)
- WriteLog("DSP: %s is enabling interrupts %s%s%s%s%s%s\n", whoName[who],
- (data & 0x010 ? "CPU " : ""), (data & 0x020 ? "I2S " : ""),
- (data & 0x040 ? "TIMER0 " : ""), (data & 0x080 ? "TIMER1 " : ""),
- (data & 0x100 ? "EXT0 " : ""), (data & 0x10000 ? "EXT1" : ""));
-/*if (data & 0x00020) // CD BIOS DSP code...
-{
-//001AC1BA: movea.l #$1AC200, A0
-//001AC1C0: move.l #$1AC68C, D0
- char buffer[512];
-
- WriteLog("\n---[DSP code at 00F1B97C]---------------------------\n");
- uint32_t j = 0xF1B97C;//0x1AC200;
- while (j <= 0xF1BE08)//0x1AC68C)
- {
- uint32_t oldj = j;
- j += dasmjag(JAGUAR_DSP, buffer, j);
-// WriteLog("\t%08X: %s\n", oldj+0xD6F77C, buffer);
- WriteLog("\t%08X: %s\n", oldj, buffer);
- }
-}//*/
-#endif
break;
}
case 0x04:
#ifdef DSP_DEBUG
WriteLog("DSP: DSP -> CPU interrupt\n");
#endif
-// This was WRONG
-// Why do we check for a valid handler at 64? Isn't that the Jag programmer's responsibility? (YES)
+
#warning "!!! DSP IRQs that go to the 68K have to be routed thru TOM !!! FIX !!!"
- if (JERRYIRQEnabled(IRQ2_DSP))// && jaguar_interrupt_handler_is_valid(64))
+ if (JERRYIRQEnabled(IRQ2_DSP))
{
JERRYSetPendingIRQ(IRQ2_DSP);
DSPReleaseTimeslice();
JaguarWriteLong(offset, data, who);
}
+
//
// Update the DSP register file pointers depending on REGPAGE bit
//
#endif
}
+
//
// Check for and handle any asserted DSP IRQs
//
FlushDSPPipeline();
}
+
//
// Non-pipelined version...
//
//!!!!!!!!
}
+
//
// Set the specified DSP IRQ line to a given state
//
// GPUSetIRQLine(GPUIRQ_DSP, ASSERT_LINE);
}
+
bool DSPIsRunning(void)
{
return (DSP_RUNNING ? true : false);
}
+
void DSPInit(void)
{
// memory_malloc_secure((void **)&dsp_ram_8, 0x2000, "DSP work RAM");
dsp_build_branch_condition_table();
DSPReset();
- srand(time(NULL)); // For randomizing local RAM
}
+
void DSPReset(void)
{
dsp_pc = 0x00F1B000;
IMASKCleared = false;
FlushDSPPipeline();
dsp_reset_stats();
-// memset(dsp_ram_8, 0xFF, 0x2000);
+
// Contents of local RAM are quasi-stable; we simulate this by randomizing RAM contents
for(uint32_t i=0; i<8192; i+=4)
- {
*((uint32_t *)(&dsp_ram_8[i])) = rand();
- }
}
+
void DSPDumpDisassembly(void)
{
char buffer[512];
}
}
+
void DSPDumpRegisters(void)
{
//Shoud add modulus, etc to dump here...
}
}
+
void DSPDone(void)
{
- int i, j;
+ WriteLog("\n\n---------------------------------------------------------------------\n");
+ WriteLog("DSP I/O Registers\n");
+ WriteLog("---------------------------------------------------------------------\n");
+ WriteLog("F1%04X (D_FLAGS): $%06X\n", 0xA100, (dsp_flags & 0xFFFFFFF8) | (dsp_flag_n << 2) | (dsp_flag_c << 1) | dsp_flag_z);
+ WriteLog("F1%04X (D_MTXC): $%04X\n", 0xA104, dsp_matrix_control);
+ WriteLog("F1%04X (D_MTXA): $%04X\n", 0xA108, dsp_pointer_to_matrix);
+ WriteLog("F1%04X (D_END): $%02X\n", 0xA10C, dsp_data_organization);
+ WriteLog("F1%04X (D_PC): $%06X\n", 0xA110, dsp_pc);
+ WriteLog("F1%04X (D_CTRL): $%06X\n", 0xA114, dsp_control);
+ WriteLog("F1%04X (D_MOD): $%08X\n", 0xA118, dsp_modulo);
+ WriteLog("F1%04X (D_REMAIN): $%08X\n", 0xA11C, dsp_remain);
+ WriteLog("F1%04X (D_DIVCTRL): $%02X\n", 0xA11C, dsp_div_control);
+ WriteLog("F1%04X (D_MACHI): $%02X\n", 0xA120, (dsp_acc >> 32) & 0xFF);
+ WriteLog("---------------------------------------------------------------------\n\n\n");
+
WriteLog("DSP: Stopped at PC=%08X dsp_modulo=%08X (dsp was%s running)\n", dsp_pc, dsp_modulo, (DSP_RUNNING ? "" : "n't"));
WriteLog("DSP: %sin interrupt handler\n", (dsp_flags & IMASK ? "" : "not "));
- // get the active interrupt bits
+ // Get the active interrupt bits
int bits = ((dsp_control >> 10) & 0x20) | ((dsp_control >> 6) & 0x1F);
- // get the interrupt mask
+ // Get the interrupt mask
int mask = ((dsp_flags >> 11) & 0x20) | ((dsp_flags >> 4) & 0x1F);
WriteLog("DSP: pending=$%X enabled=$%X (%s%s%s%s%s%s)\n", bits, mask,
(mask & 0x01 ? "CPU " : ""), (mask & 0x02 ? "I2S " : ""),
(mask & 0x04 ? "Timer0 " : ""), (mask & 0x08 ? "Timer1 " : ""),
(mask & 0x10 ? "Ext0 " : ""), (mask & 0x20 ? "Ext1" : ""));
- WriteLog("\nRegisters bank 0\n");
-
- for(int j=0; j<8; j++)
- {
- WriteLog("\tR%02i=%08X R%02i=%08X R%02i=%08X R%02i=%08X\n",
- (j << 2) + 0, dsp_reg_bank_0[(j << 2) + 0],
- (j << 2) + 1, dsp_reg_bank_0[(j << 2) + 1],
- (j << 2) + 2, dsp_reg_bank_0[(j << 2) + 2],
- (j << 2) + 3, dsp_reg_bank_0[(j << 2) + 3]);
- }
-
- WriteLog("\nRegisters bank 1\n");
-
- for (j=0; j<8; j++)
- {
- WriteLog("\tR%02i=%08X R%02i=%08X R%02i=%08X R%02i=%08X\n",
- (j << 2) + 0, dsp_reg_bank_1[(j << 2) + 0],
- (j << 2) + 1, dsp_reg_bank_1[(j << 2) + 1],
- (j << 2) + 2, dsp_reg_bank_1[(j << 2) + 2],
- (j << 2) + 3, dsp_reg_bank_1[(j << 2) + 3]);
- }
-
+ DSPDumpRegisters();
WriteLog("\n");
static char buffer[512];
- j = DSP_WORK_RAM_BASE;
+ int j = DSP_WORK_RAM_BASE;
while (j <= 0xF1CFFF)
{
uint32_t oldj = j;
j += dasmjag(JAGUAR_DSP, buffer, j);
WriteLog("\t%08X: %s\n", oldj, buffer);
- }//*/
+ }
WriteLog("DSP opcodes use:\n");
- for (i=0;i<64;i++)
+ for(int i=0; i<64; i++)
{
if (dsp_opcode_use[i])
WriteLog("\t%s %i\n", dsp_opcode_str[i], dsp_opcode_use[i]);
- }//*/
+ }
}
dsp_in_exec--;
}
+
//
// DSP opcode handlers
//
#endif
}
+
static void dsp_opcode_jr(void)
{
#ifdef DSP_DIS_JR
#endif
}
+
static void dsp_opcode_add(void)
{
#ifdef DSP_DIS_ADD
#endif
}
+
static void dsp_opcode_addc(void)
{
#ifdef DSP_DIS_ADDC
#endif
}
+
static void dsp_opcode_addq(void)
{
#ifdef DSP_DIS_ADDQ
#endif
}
+
static void dsp_opcode_sub(void)
{
#ifdef DSP_DIS_SUB
#endif
}
+
static void dsp_opcode_subc(void)
{
#ifdef DSP_DIS_SUBC
if (doDSPDis)
WriteLog("%06X: SUBC R%02u, R%02u [NCZ:%u%u%u, R%02u=%08X, R%02u=%08X] -> ", dsp_pc-2, IMM_1, IMM_2, dsp_flag_n, dsp_flag_c, dsp_flag_z, IMM_1, RM, IMM_2, RN);
#endif
- uint32_t res = RN - RM - dsp_flag_c;
- uint32_t borrow = dsp_flag_c;
- SET_ZNC_SUB(RN - borrow, RM, res);
- RN = res;
+ // This is how the DSP ALU does it--Two's complement with inverted carry
+ uint64_t res = (uint64_t)RN + (uint64_t)(RM ^ 0xFFFFFFFF) + (dsp_flag_c ^ 1);
+ // Carry out of the result is inverted too
+ dsp_flag_c = ((res >> 32) & 0x01) ^ 1;
+ RN = (res & 0xFFFFFFFF);
+ SET_ZN(RN);
#ifdef DSP_DIS_SUBC
if (doDSPDis)
WriteLog("[NCZ:%u%u%u, R%02u=%08X, R%02u=%08X]\n", dsp_flag_n, dsp_flag_c, dsp_flag_z, IMM_1, RM, IMM_2, RN);
#endif
}
+
static void dsp_opcode_subq(void)
{
#ifdef DSP_DIS_SUBQ
#endif
}
+
static void dsp_opcode_cmp(void)
{
#ifdef DSP_DIS_CMP
#endif
}
+
static void dsp_opcode_cmpq(void)
{
static int32_t sqtable[32] =
#endif
}
+
static void dsp_opcode_and(void)
{
#ifdef DSP_DIS_AND
#endif
}
+
static void dsp_opcode_or(void)
{
#ifdef DSP_DIS_OR
#endif
}
+
static void dsp_opcode_xor(void)
{
#ifdef DSP_DIS_XOR
#endif
}
+
static void dsp_opcode_not(void)
{
#ifdef DSP_DIS_NOT
#endif
}
+
static void dsp_opcode_move_pc(void)
{
RN = dsp_pc - 2;
}
+
static void dsp_opcode_store_r14_indexed(void)
{
#ifdef DSP_DIS_STORE14I
#endif
}
+
static void dsp_opcode_store_r15_indexed(void)
{
#ifdef DSP_DIS_STORE15I
#endif
}
+
static void dsp_opcode_load_r14_ri(void)
{
#ifdef DSP_DIS_LOAD14R
#endif
}
+
static void dsp_opcode_load_r15_ri(void)
{
#ifdef DSP_DIS_LOAD15R
#endif
}
+
static void dsp_opcode_store_r14_ri(void)
{
DSPWriteLong(dsp_reg[14] + RM, RN, DSP);
}
+
static void dsp_opcode_store_r15_ri(void)
{
DSPWriteLong(dsp_reg[15] + RM, RN, DSP);
}
+
static void dsp_opcode_nop(void)
{
#ifdef DSP_DIS_NOP
#endif
}
+
static void dsp_opcode_storeb(void)
{
#ifdef DSP_DIS_STOREB
JaguarWriteByte(RM, RN, DSP);
}
+
static void dsp_opcode_storew(void)
{
#ifdef DSP_DIS_STOREW
#endif
}
+
static void dsp_opcode_store(void)
{
#ifdef DSP_DIS_STORE
#endif
}
+
static void dsp_opcode_loadb(void)
{
#ifdef DSP_DIS_LOADB
#endif
}
+
static void dsp_opcode_loadw(void)
{
#ifdef DSP_DIS_LOADW
#endif
}
+
static void dsp_opcode_load(void)
{
#ifdef DSP_DIS_LOAD
#endif
}
+
static void dsp_opcode_load_r14_indexed(void)
{
#ifdef DSP_DIS_LOAD14I
#endif
}
+
static void dsp_opcode_load_r15_indexed(void)
{
#ifdef DSP_DIS_LOAD15I
#endif
}
+
static void dsp_opcode_movei(void)
{
#ifdef DSP_DIS_MOVEI
#endif
}
+
static void dsp_opcode_moveta(void)
{
#ifdef DSP_DIS_MOVETA
#endif
}
+
static void dsp_opcode_movefa(void)
{
#ifdef DSP_DIS_MOVEFA
#endif
}
+
static void dsp_opcode_move(void)
{
#ifdef DSP_DIS_MOVE
#endif
}
+
static void dsp_opcode_moveq(void)
{
#ifdef DSP_DIS_MOVEQ
#endif
}
+
static void dsp_opcode_resmac(void)
{
#ifdef DSP_DIS_RESMAC
#endif
}
+
static void dsp_opcode_imult(void)
{
#ifdef DSP_DIS_IMULT
#endif
}
+
static void dsp_opcode_mult(void)
{
#ifdef DSP_DIS_MULT
#endif
}
+
static void dsp_opcode_bclr(void)
{
#ifdef DSP_DIS_BCLR
#endif
}
+
static void dsp_opcode_btst(void)
{
#ifdef DSP_DIS_BTST
#endif
}
+
static void dsp_opcode_bset(void)
{
#ifdef DSP_DIS_BSET
#endif
}
+
static void dsp_opcode_subqt(void)
{
#ifdef DSP_DIS_SUBQT
#endif
}
+
static void dsp_opcode_addqt(void)
{
#ifdef DSP_DIS_ADDQT
#endif
}
+
static void dsp_opcode_imacn(void)
{
#ifdef DSP_DIS_IMACN
#endif
}
+
static void dsp_opcode_mtoi(void)
{
RN = (((int32_t)RM >> 8) & 0xFF800000) | (RM & 0x007FFFFF);
SET_ZN(RN);
}
+
static void dsp_opcode_normi(void)
{
uint32_t _Rm = RM;
SET_ZN(RN);
}
+
static void dsp_opcode_mmult(void)
{
int count = dsp_matrix_control&0x0f;
SET_ZN(RN);
}
+
static void dsp_opcode_abs(void)
{
#ifdef DSP_DIS_ABS
#endif
}
+
static void dsp_opcode_div(void)
{
- uint32_t _Rm=RM;
- uint32_t _Rn=RN;
-
- if (_Rm)
+#if 0
+ if (RM)
{
- if (dsp_div_control & 1)
+ if (dsp_div_control & 0x01) // 16.16 division
{
- dsp_remain = (((uint64_t)_Rn) << 16) % _Rm;
- if (dsp_remain&0x80000000)
- dsp_remain-=_Rm;
- RN = (((uint64_t)_Rn) << 16) / _Rm;
+ dsp_remain = ((uint64_t)RN << 16) % RM;
+ RN = ((uint64_t)RN << 16) / RM;
}
else
{
- dsp_remain = _Rn % _Rm;
- if (dsp_remain&0x80000000)
- dsp_remain-=_Rm;
- RN/=_Rm;
+ // We calculate the remainder first because we destroy RN after
+ // this by assigning it to itself.
+ dsp_remain = RN % RM;
+ RN = RN / RM;
}
+
}
else
- RN=0xffffffff;
+ {
+ // This is what happens according to SCPCD. NYAN!
+ RN = 0xFFFFFFFF;
+ dsp_remain = 0;
+ }
+#else
+ // Real algorithm, courtesy of SCPCD: NYAN!
+ uint32_t q = RN;
+ uint32_t r = 0;
+
+ // If 16.16 division, stuff top 16 bits of RN into remainder and put the
+ // bottom 16 of RN in top 16 of quotient
+ if (dsp_div_control & 0x01)
+ q <<= 16, r = RN >> 16;
+
+ for(int i=0; i<32; i++)
+ {
+// uint32_t sign = (r >> 31) & 0x01;
+ uint32_t sign = r & 0x80000000;
+ r = (r << 1) | ((q >> 31) & 0x01);
+ r += (sign ? RM : -RM);
+ q = (q << 1) | (((~r) >> 31) & 0x01);
+ }
+
+ RN = q;
+ dsp_remain = r;
+#endif
}
+
static void dsp_opcode_imultn(void)
{
#ifdef DSP_DIS_IMULTN
#endif
}
+
static void dsp_opcode_neg(void)
{
#ifdef DSP_DIS_NEG
#endif
}
+
static void dsp_opcode_shlq(void)
{
#ifdef DSP_DIS_SHLQ
#endif
}
+
static void dsp_opcode_shrq(void)
{
#ifdef DSP_DIS_SHRQ
#endif
}
+
static void dsp_opcode_ror(void)
{
#ifdef DSP_DIS_ROR
#endif
}
+
static void dsp_opcode_rorq(void)
{
#ifdef DSP_DIS_RORQ
#endif
}
+
static void dsp_opcode_sha(void)
{
int32_t sRm=(int32_t)RM;
SET_ZN(RN);
}
+
static void dsp_opcode_sharq(void)
{
#ifdef DSP_DIS_SHARQ
#endif
}
+
static void dsp_opcode_sh(void)
{
int32_t sRm=(int32_t)RM;