BizHawk/waterbox/virtualjaguar/src/dsp.cpp

608 lines
15 KiB
C++

//
// DSP core
//
// Originally by David Raingeard
// GCC/SDL port by Niels Wagenaar (Linux/WIN32) and Caz (BeOS)
// Extensive cleanups/rewrites by James Hammons
// (C) 2010 Underground Software
//
// JLH = James Hammons <jlhamm@acm.org>
//
// Who When What
// --- ---------- -------------------------------------------------------------
// JLH 01/16/2010 Created this log ;-)
// JLH 11/26/2011 Added fixes for LOAD/STORE alignment issues
//
#include "dsp.h"
#include <stdlib.h>
#include "dac.h"
#include "gpu.h"
#include "jaguar.h"
#include "jerry.h"
#include "m68000/m68kinterface.h"
// DSP flags (old--have to get rid of this crap)
#define CINT0FLAG 0x00200
#define CINT1FLAG 0x00400
#define CINT2FLAG 0x00800
#define CINT3FLAG 0x01000
#define CINT4FLAG 0x02000
#define CINT04FLAGS (CINT0FLAG | CINT1FLAG | CINT2FLAG | CINT3FLAG | CINT4FLAG)
#define CINT5FLAG 0x20000 /* DSP only */
// DSP_FLAGS bits
#define ZERO_FLAG 0x00001
#define CARRY_FLAG 0x00002
#define NEGA_FLAG 0x00004
#define IMASK 0x00008
#define INT_ENA0 0x00010
#define INT_ENA1 0x00020
#define INT_ENA2 0x00040
#define INT_ENA3 0x00080
#define INT_ENA4 0x00100
#define INT_CLR0 0x00200
#define INT_CLR1 0x00400
#define INT_CLR2 0x00800
#define INT_CLR3 0x01000
#define INT_CLR4 0x02000
#define REGPAGE 0x04000
#define DMAEN 0x08000
#define INT_ENA5 0x10000
#define INT_CLR5 0x20000
// DSP_CTRL bits
#define DSPGO 0x00001
#define CPUINT 0x00002
#define DSPINT0 0x00004
#define SINGLE_STEP 0x00008
#define SINGLE_GO 0x00010
// Bit 5 is unused!
#define INT_LAT0 0x00040
#define INT_LAT1 0x00080
#define INT_LAT2 0x00100
#define INT_LAT3 0x00200
#define INT_LAT4 0x00400
#define BUS_HOG 0x00800
#define VERSION 0x0F000
#define INT_LAT5 0x10000
static void dsp_opcode_abs(void);
static void dsp_opcode_add(void);
static void dsp_opcode_addc(void);
static void dsp_opcode_addq(void);
static void dsp_opcode_addqmod(void);
static void dsp_opcode_addqt(void);
static void dsp_opcode_and(void);
static void dsp_opcode_bclr(void);
static void dsp_opcode_bset(void);
static void dsp_opcode_btst(void);
static void dsp_opcode_cmp(void);
static void dsp_opcode_cmpq(void);
static void dsp_opcode_div(void);
static void dsp_opcode_imacn(void);
static void dsp_opcode_imult(void);
static void dsp_opcode_imultn(void);
static void dsp_opcode_jr(void);
static void dsp_opcode_jump(void);
static void dsp_opcode_load(void);
static void dsp_opcode_loadb(void);
static void dsp_opcode_loadw(void);
static void dsp_opcode_load_r14_indexed(void);
static void dsp_opcode_load_r14_ri(void);
static void dsp_opcode_load_r15_indexed(void);
static void dsp_opcode_load_r15_ri(void);
static void dsp_opcode_mirror(void);
static void dsp_opcode_mmult(void);
static void dsp_opcode_move(void);
static void dsp_opcode_movei(void);
static void dsp_opcode_movefa(void);
static void dsp_opcode_move_pc(void);
static void dsp_opcode_moveq(void);
static void dsp_opcode_moveta(void);
static void dsp_opcode_mtoi(void);
static void dsp_opcode_mult(void);
static void dsp_opcode_neg(void);
static void dsp_opcode_nop(void);
static void dsp_opcode_normi(void);
static void dsp_opcode_not(void);
static void dsp_opcode_or(void);
static void dsp_opcode_resmac(void);
static void dsp_opcode_ror(void);
static void dsp_opcode_rorq(void);
static void dsp_opcode_xor(void);
static void dsp_opcode_sat16s(void);
static void dsp_opcode_sat32s(void);
static void dsp_opcode_sh(void);
static void dsp_opcode_sha(void);
static void dsp_opcode_sharq(void);
static void dsp_opcode_shlq(void);
static void dsp_opcode_shrq(void);
static void dsp_opcode_store(void);
static void dsp_opcode_storeb(void);
static void dsp_opcode_storew(void);
static void dsp_opcode_store_r14_indexed(void);
static void dsp_opcode_store_r14_ri(void);
static void dsp_opcode_store_r15_indexed(void);
static void dsp_opcode_store_r15_ri(void);
static void dsp_opcode_sub(void);
static void dsp_opcode_subc(void);
static void dsp_opcode_subq(void);
static void dsp_opcode_subqmod(void);
static void dsp_opcode_subqt(void);
static void dsp_opcode_illegal(void);
static const uint8_t dsp_opcode_cycles[64] =
{
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 9, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2,
2, 2, 2, 3, 3, 1, 1, 1,
1, 1, 1, 1, 1, 1, 4, 1,
1, 1, 3, 3, 1, 1, 1, 1
};
static void (* dsp_opcode[64])() =
{
dsp_opcode_add, dsp_opcode_addc, dsp_opcode_addq, dsp_opcode_addqt,
dsp_opcode_sub, dsp_opcode_subc, dsp_opcode_subq, dsp_opcode_subqt,
dsp_opcode_neg, dsp_opcode_and, dsp_opcode_or, dsp_opcode_xor,
dsp_opcode_not, dsp_opcode_btst, dsp_opcode_bset, dsp_opcode_bclr,
dsp_opcode_mult, dsp_opcode_imult, dsp_opcode_imultn, dsp_opcode_resmac,
dsp_opcode_imacn, dsp_opcode_div, dsp_opcode_abs, dsp_opcode_sh,
dsp_opcode_shlq, dsp_opcode_shrq, dsp_opcode_sha, dsp_opcode_sharq,
dsp_opcode_ror, dsp_opcode_rorq, dsp_opcode_cmp, dsp_opcode_cmpq,
dsp_opcode_subqmod, dsp_opcode_sat16s, dsp_opcode_move, dsp_opcode_moveq,
dsp_opcode_moveta, dsp_opcode_movefa, dsp_opcode_movei, dsp_opcode_loadb,
dsp_opcode_loadw, dsp_opcode_load, dsp_opcode_sat32s, dsp_opcode_load_r14_indexed,
dsp_opcode_load_r15_indexed, dsp_opcode_storeb, dsp_opcode_storew, dsp_opcode_store,
dsp_opcode_mirror, dsp_opcode_store_r14_indexed, dsp_opcode_store_r15_indexed, dsp_opcode_move_pc,
dsp_opcode_jump, dsp_opcode_jr, dsp_opcode_mmult, dsp_opcode_mtoi,
dsp_opcode_normi, dsp_opcode_nop, dsp_opcode_load_r14_ri, dsp_opcode_load_r15_ri,
dsp_opcode_store_r14_ri, dsp_opcode_store_r15_ri, dsp_opcode_illegal, dsp_opcode_addqmod,
};
uint32_t dsp_pc;
static uint64_t dsp_acc;
static uint32_t dsp_remain;
static uint32_t dsp_modulo;
static uint32_t dsp_flags;
static uint32_t dsp_matrix_control;
static uint32_t dsp_pointer_to_matrix;
static uint32_t dsp_data_organization;
static uint32_t dsp_control;
static uint32_t dsp_div_control;
static uint8_t dsp_flag_z, dsp_flag_n, dsp_flag_c;
static uint32_t * dsp_reg = NULL, * dsp_alternate_reg = NULL;
uint32_t dsp_reg_bank_0[32], dsp_reg_bank_1[32];
static uint32_t dsp_opcode_first_parameter;
static uint32_t dsp_opcode_second_parameter;
static bool IMASKCleared;
static uint32_t dsp_inhibit_interrupt;
#define DSP_RUNNING (dsp_control & 0x01)
static uint8_t branch_condition_table[32 * 8];
static uint16_t mirror_table[65536];
uint8_t dsp_ram_8[0x2000];
static void dsp_build_branch_condition_table(void)
{
for(int i=0; i<65536; i++)
{
mirror_table[i] = ((i >> 15) & 0x0001) | ((i >> 13) & 0x0002)
| ((i >> 11) & 0x0004) | ((i >> 9) & 0x0008)
| ((i >> 7) & 0x0010) | ((i >> 5) & 0x0020)
| ((i >> 3) & 0x0040) | ((i >> 1) & 0x0080)
| ((i << 1) & 0x0100) | ((i << 3) & 0x0200)
| ((i << 5) & 0x0400) | ((i << 7) & 0x0800)
| ((i << 9) & 0x1000) | ((i << 11) & 0x2000)
| ((i << 13) & 0x4000) | ((i << 15) & 0x8000);
}
for(int i=0; i<8; i++)
{
for(int j=0; j<32; j++)
{
int result = 1;
if ((j & 1) && (i & ZERO_FLAG))
result = 0;
if ((j & 2) && (!(i & ZERO_FLAG)))
result = 0;
if ((j & 4) && (i & (CARRY_FLAG << (j >> 4))))
result = 0;
if ((j & 8) && (!(i & (CARRY_FLAG << (j >> 4)))))
result = 0;
branch_condition_table[i * 32 + j] = result;
}
}
}
//
// Update the DSP register file pointers depending on REGPAGE bit
//
static void DSPUpdateRegisterBanks(void)
{
int bank = (dsp_flags & REGPAGE);
if (dsp_flags & IMASK)
bank = 0;
if (bank)
dsp_reg = dsp_reg_bank_1, dsp_alternate_reg = dsp_reg_bank_0;
else
dsp_reg = dsp_reg_bank_0, dsp_alternate_reg = dsp_reg_bank_1;
}
//
// Check for and handle any asserted DSP IRQs
//
static void DSPHandleIRQs(void)
{
if (dsp_flags & IMASK)
return;
uint32_t bits = ((dsp_control >> 10) & 0x20) | ((dsp_control >> 6) & 0x1F),
mask = ((dsp_flags >> 11) & 0x20) | ((dsp_flags >> 4) & 0x1F);
bits &= mask;
if (!bits)
return;
int which = 0;
if (bits & 0x01)
which = 0;
if (bits & 0x02)
which = 1;
if (bits & 0x04)
which = 2;
if (bits & 0x08)
which = 3;
if (bits & 0x10)
which = 4;
if (bits & 0x20)
which = 5;
dsp_flags |= IMASK;
DSPUpdateRegisterBanks();
dsp_reg[31] -= 4;
dsp_reg[30] = dsp_pc - 2;
DSPWriteLong(dsp_reg[31], dsp_reg[30], DSP);
dsp_pc = dsp_reg[30] = DSP_WORK_RAM_BASE + (which * 0x10);
}
uint8_t DSPReadByte(uint32_t offset, uint32_t who)
{
if (offset >= DSP_WORK_RAM_BASE && offset <= (DSP_WORK_RAM_BASE + 0x1FFF))
return dsp_ram_8[offset - DSP_WORK_RAM_BASE];
if (offset >= DSP_CONTROL_RAM_BASE && offset <= (DSP_CONTROL_RAM_BASE + 0x1F))
{
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);
}
return JaguarReadByte(offset, who);
}
uint16_t DSPReadWord(uint32_t offset, uint32_t who)
{
offset &= 0xFFFFFFFE;
if (offset >= DSP_WORK_RAM_BASE && offset <= DSP_WORK_RAM_BASE+0x1FFF)
{
offset -= DSP_WORK_RAM_BASE;
return GET16(dsp_ram_8, offset);
}
else if ((offset>=DSP_CONTROL_RAM_BASE)&&(offset<DSP_CONTROL_RAM_BASE+0x20))
{
uint32_t data = DSPReadLong(offset & 0xFFFFFFFC, who);
if (offset & 0x03)
return data & 0xFFFF;
else
return data >> 16;
}
return JaguarReadWord(offset, who);
}
uint32_t DSPReadLong(uint32_t offset, uint32_t who)
{
offset &= 0xFFFFFFFC;
if (offset >= DSP_WORK_RAM_BASE && offset <= DSP_WORK_RAM_BASE + 0x1FFF)
{
offset -= DSP_WORK_RAM_BASE;
return GET32(dsp_ram_8, offset);
}
if (offset >= DSP_CONTROL_RAM_BASE && offset <= DSP_CONTROL_RAM_BASE + 0x23)
{
offset &= 0x3F;
switch (offset)
{
case 0x00:
dsp_flags = (dsp_flags & 0xFFFFFFF8) | (dsp_flag_n << 2) | (dsp_flag_c << 1) | dsp_flag_z;
return dsp_flags & 0xFFFFC1FF;
case 0x04: return dsp_matrix_control;
case 0x08: return dsp_pointer_to_matrix;
case 0x0C: return dsp_data_organization;
case 0x10: return dsp_pc;
case 0x14: return dsp_control;
case 0x18: return dsp_modulo;
case 0x1C: return dsp_remain;
case 0x20:
return (int32_t)((int8_t)(dsp_acc >> 32));
}
return 0xFFFFFFFF;
}
return JaguarReadLong(offset, who);
}
void DSPWriteByte(uint32_t offset, uint8_t data, uint32_t who)
{
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))
{
uint32_t reg = offset & 0x1C;
int bytenum = offset & 0x03;
if ((reg >= 0x1C) && (reg <= 0x1F))
dsp_div_control = (dsp_div_control & (~(0xFF << (bytenum << 3)))) | (data << (bytenum << 3));
else
{
uint32_t old_data = DSPReadLong(offset&0xFFFFFFC, who);
bytenum = 3 - bytenum;
old_data = (old_data & (~(0xFF << (bytenum << 3)))) | (data << (bytenum << 3));
DSPWriteLong(offset & 0xFFFFFFC, old_data, who);
}
return;
}
JaguarWriteByte(offset, data, who);
}
void DSPWriteWord(uint32_t offset, uint16_t data, uint32_t who)
{
offset &= 0xFFFFFFFE;
if ((offset >= DSP_WORK_RAM_BASE) && (offset < DSP_WORK_RAM_BASE+0x2000))
{
offset -= DSP_WORK_RAM_BASE;
dsp_ram_8[offset] = data >> 8;
dsp_ram_8[offset+1] = data & 0xFF;
return;
}
else if ((offset >= DSP_CONTROL_RAM_BASE) && (offset < DSP_CONTROL_RAM_BASE+0x20))
{
if ((offset & 0x1C) == 0x1C)
{
if (offset & 0x03)
dsp_div_control = (dsp_div_control & 0xFFFF0000) | (data & 0xFFFF);
else
dsp_div_control = (dsp_div_control & 0xFFFF) | ((data & 0xFFFF) << 16);
}
else
{
uint32_t old_data = DSPReadLong(offset & 0xFFFFFFC, who);
if (offset & 0x03)
old_data = (old_data & 0xFFFF0000) | (data & 0xFFFF);
else
old_data = (old_data & 0xFFFF) | ((data & 0xFFFF) << 16);
DSPWriteLong(offset & 0xFFFFFFC, old_data, who);
}
return;
}
JaguarWriteWord(offset, data, who);
}
void DSPWriteLong(uint32_t offset, uint32_t data, uint32_t who)
{
offset &= 0xFFFFFFFC;
if (offset >= DSP_WORK_RAM_BASE && offset <= DSP_WORK_RAM_BASE + 0x1FFF)
{
offset -= DSP_WORK_RAM_BASE;
SET32(dsp_ram_8, offset, data);
return;
}
else if (offset >= DSP_CONTROL_RAM_BASE && offset <= (DSP_CONTROL_RAM_BASE + 0x1F))
{
offset &= 0x1F;
switch (offset)
{
case 0x00:
{
IMASKCleared |= (dsp_flags & IMASK) && !(data & IMASK);
dsp_flags = data & (~IMASK);
dsp_flag_z = dsp_flags & 0x01;
dsp_flag_c = (dsp_flags >> 1) & 0x01;
dsp_flag_n = (dsp_flags >> 2) & 0x01;
DSPUpdateRegisterBanks();
dsp_control &= ~((dsp_flags & CINT04FLAGS) >> 3);
dsp_control &= ~((dsp_flags & CINT5FLAG) >> 1);
break;
}
case 0x04:
dsp_matrix_control = data;
break;
case 0x08:
dsp_pointer_to_matrix = 0xF1B000 | (data & 0x000FFC);
break;
case 0x0C:
dsp_data_organization = data;
break;
case 0x10:
dsp_pc = data;
break;
case 0x14:
{
bool wasRunning = DSP_RUNNING;
if (data & CPUINT)
{
if (JERRYIRQEnabled(IRQ2_DSP))
{
JERRYSetPendingIRQ(IRQ2_DSP);
m68k_set_irq(2);
}
data &= ~CPUINT;
}
if (data & DSPINT0)
{
m68k_end_timeslice();
DSPSetIRQLine(DSPIRQ_CPU, ASSERT_LINE);
data &= ~DSPINT0;
}
uint32_t mask = VERSION | INT_LAT0 | INT_LAT1 | INT_LAT2 | INT_LAT3 | INT_LAT4 | INT_LAT5;
dsp_control = (dsp_control & mask) | (data & ~mask);
if (DSP_RUNNING)
{
if (who == M68K)
m68k_end_timeslice();
}
break;
}
case 0x18:
dsp_modulo = data;
break;
case 0x1C:
dsp_div_control = data;
break;
}
return;
}
JaguarWriteLong(offset, data, who);
}
//
// Set the specified DSP IRQ line to a given state
//
void DSPSetIRQLine(int irqline, int state)
{
uint32_t mask = INT_LAT0 << irqline;
dsp_control &= ~mask;
if (state)
{
dsp_control |= mask;
DSPHandleIRQs();
}
}
bool DSPIsRunning(void)
{
return (DSP_RUNNING ? true : false);
}
void DSPInit(void)
{
dsp_build_branch_condition_table();
DSPReset();
}
void DSPReset(void)
{
dsp_pc = 0x00F1B000;
dsp_acc = 0x00000000;
dsp_remain = 0x00000000;
dsp_modulo = 0xFFFFFFFF;
dsp_flags = 0x00040000;
dsp_matrix_control = 0x00000000;
dsp_pointer_to_matrix = 0x00000000;
dsp_data_organization = 0xFFFFFFFF;
dsp_control = 0x00002000;
dsp_div_control = 0x00000000;
dsp_reg = dsp_reg_bank_0;
dsp_alternate_reg = dsp_reg_bank_1;
for(int i=0; i<32; i++)
dsp_reg[i] = dsp_alternate_reg[i] = 0x00000000;
dsp_flag_z = dsp_flag_n = dsp_flag_c = 0;
IMASKCleared = false;
for(uint32_t i=0; i<8192; i+=4)
*((uint32_t *)(&dsp_ram_8[i])) = rand();
}
//
// DSP execution core
//
void DSPExec(int32_t cycles)
{
while (cycles > 0 && DSP_RUNNING)
{
MAYBE_CALLBACK(DSPTraceCallback, dsp_pc, dsp_reg);
if (IMASKCleared && !dsp_inhibit_interrupt)
{
DSPHandleIRQs();
IMASKCleared = false;
}
dsp_inhibit_interrupt = 0;
uint16_t opcode = DSPReadWord(dsp_pc, DSP);
uint32_t index = opcode >> 10;
dsp_opcode_first_parameter = (opcode >> 5) & 0x1F;
dsp_opcode_second_parameter = opcode & 0x1F;
dsp_pc += 2;
dsp_opcode[index]();
cycles -= dsp_opcode_cycles[index];
}
}
//
// DSP opcodes
//
#define RISC 2
#include "risc_opcodes.h"