2009-03-23 09:10:32 +00:00
|
|
|
/*====================================================================
|
|
|
|
|
|
|
|
filename: gdsp_interpreter.cpp
|
|
|
|
project: GCemu
|
|
|
|
created: 2004-6-18
|
|
|
|
mail: duddie@walla.com
|
|
|
|
|
|
|
|
Copyright (c) 2005 Duddie & Tratax
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
====================================================================*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2009-04-01 20:22:43 +00:00
|
|
|
#include "DSPTables.h"
|
2009-04-08 17:58:58 +00:00
|
|
|
#include "DSPAnalyzer.h"
|
2009-04-01 20:22:43 +00:00
|
|
|
|
2009-03-23 09:10:32 +00:00
|
|
|
#include "gdsp_interface.h"
|
|
|
|
#include "gdsp_opcodes_helper.h"
|
|
|
|
#include "Tools.h"
|
2009-04-06 18:47:21 +00:00
|
|
|
#include "MemoryUtil.h"
|
2009-03-23 09:10:32 +00:00
|
|
|
|
|
|
|
//-------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
SDSP g_dsp;
|
|
|
|
|
2009-04-08 20:26:33 +00:00
|
|
|
volatile u32 gdsp_running;
|
2009-04-05 09:23:43 +00:00
|
|
|
|
2009-04-05 11:55:18 +00:00
|
|
|
static bool cr_halt = true;
|
|
|
|
static bool cr_external_int = false;
|
|
|
|
|
2009-03-23 09:10:32 +00:00
|
|
|
void UpdateCachedCR()
|
|
|
|
{
|
2009-04-05 11:55:18 +00:00
|
|
|
cr_halt = (g_dsp.cr & 0x4) != 0;
|
|
|
|
cr_external_int = (g_dsp.cr & 0x02) != 0;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void gdsp_init()
|
|
|
|
{
|
2009-04-06 18:47:21 +00:00
|
|
|
// Dump IMEM when ucodes get uploaded. Why not... still a plugin heavily in dev.
|
|
|
|
g_dsp.dump_imem = true;
|
2009-03-23 09:10:32 +00:00
|
|
|
|
2009-04-06 18:47:21 +00:00
|
|
|
g_dsp.irom = (u16*)AllocateMemoryPages(DSP_IROM_BYTE_SIZE);
|
|
|
|
g_dsp.iram = (u16*)AllocateMemoryPages(DSP_IRAM_BYTE_SIZE);
|
|
|
|
g_dsp.dram = (u16*)AllocateMemoryPages(DSP_DRAM_BYTE_SIZE);
|
|
|
|
g_dsp.coef = (u16*)AllocateMemoryPages(DSP_COEF_BYTE_SIZE);
|
2009-03-23 09:10:32 +00:00
|
|
|
|
2009-04-05 11:55:18 +00:00
|
|
|
// Fill roms with zeros.
|
2009-04-06 18:47:21 +00:00
|
|
|
memset(g_dsp.irom, 0, DSP_IROM_BYTE_SIZE);
|
|
|
|
memset(g_dsp.coef, 0, DSP_COEF_BYTE_SIZE);
|
2009-04-05 11:55:18 +00:00
|
|
|
|
2009-03-23 09:10:32 +00:00
|
|
|
for (int i = 0; i < 32; i++)
|
|
|
|
{
|
|
|
|
g_dsp.r[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
g_dsp.reg_stack_ptr[i] = 0;
|
|
|
|
|
|
|
|
for (int j = 0; j < DSP_STACK_DEPTH; j++)
|
|
|
|
{
|
|
|
|
g_dsp.reg_stack[i][j] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-08 17:58:58 +00:00
|
|
|
// Fill IRAM with HALT opcodes.
|
2009-04-06 18:47:21 +00:00
|
|
|
for (int i = 0; i < DSP_IRAM_SIZE; i++)
|
|
|
|
{
|
|
|
|
g_dsp.iram[i] = 0x0021; // HALT opcode
|
|
|
|
}
|
|
|
|
|
2009-04-08 17:58:58 +00:00
|
|
|
// Just zero out DRAM.
|
2009-04-06 18:47:21 +00:00
|
|
|
for (int i = 0; i < DSP_DRAM_SIZE; i++)
|
|
|
|
{
|
2009-04-08 21:46:54 +00:00
|
|
|
g_dsp.dram[i] = 0x2100;
|
2009-04-06 18:47:21 +00:00
|
|
|
}
|
|
|
|
|
2009-03-23 09:10:32 +00:00
|
|
|
// copied from a real console after the custom UCode has been loaded
|
|
|
|
g_dsp.r[0x08] = 0xffff;
|
|
|
|
g_dsp.r[0x09] = 0xffff;
|
|
|
|
g_dsp.r[0x0a] = 0xffff;
|
|
|
|
g_dsp.r[0x0b] = 0xffff;
|
|
|
|
|
|
|
|
g_dsp.cr = 0x804;
|
|
|
|
gdsp_ifx_init();
|
|
|
|
|
|
|
|
UpdateCachedCR();
|
2009-04-06 18:47:21 +00:00
|
|
|
|
|
|
|
// Mostly keep IRAM write protected. We unprotect only when DMA-ing
|
|
|
|
// in new ucodes.
|
|
|
|
WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
2009-04-08 17:58:58 +00:00
|
|
|
DSPAnalyzer::Analyze();
|
2009-04-06 18:47:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gdsp_shutdown()
|
|
|
|
{
|
|
|
|
FreeMemoryPages(g_dsp.irom, DSP_IROM_BYTE_SIZE);
|
|
|
|
FreeMemoryPages(g_dsp.iram, DSP_IRAM_BYTE_SIZE);
|
|
|
|
FreeMemoryPages(g_dsp.dram, DSP_DRAM_BYTE_SIZE);
|
|
|
|
FreeMemoryPages(g_dsp.coef, DSP_COEF_BYTE_SIZE);
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void gdsp_reset()
|
|
|
|
{
|
2009-04-05 11:55:18 +00:00
|
|
|
_assert_msg_(MASTER_LOG, !g_dsp.exception_in_progress_hack, "reset while exception");
|
2009-03-23 09:10:32 +00:00
|
|
|
g_dsp.pc = DSP_RESET_VECTOR;
|
|
|
|
g_dsp.exception_in_progress_hack = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-30 03:27:31 +00:00
|
|
|
void gdsp_generate_exception(u8 level)
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
2009-04-05 09:23:43 +00:00
|
|
|
g_dsp.exceptions |= 1 << level;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
|
|
|
|
2009-04-06 18:47:21 +00:00
|
|
|
bool gdsp_load_irom(const char *fname)
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
2009-04-05 11:55:18 +00:00
|
|
|
FILE *pFile = fopen(fname, "rb");
|
2009-03-23 09:10:32 +00:00
|
|
|
if (pFile)
|
|
|
|
{
|
2009-04-05 11:55:18 +00:00
|
|
|
size_t size_in_bytes = DSP_IROM_SIZE * sizeof(u16);
|
|
|
|
size_t read_bytes = fread(g_dsp.irom, 1, size_in_bytes, pFile);
|
|
|
|
if (read_bytes != size_in_bytes)
|
|
|
|
{
|
|
|
|
PanicAlert("IROM too short : %i/%i", (int)read_bytes, (int)size_in_bytes);
|
|
|
|
fclose(pFile);
|
|
|
|
return false;
|
|
|
|
}
|
2009-03-23 09:10:32 +00:00
|
|
|
fclose(pFile);
|
2009-04-08 21:46:54 +00:00
|
|
|
|
|
|
|
// Byteswap the rom.
|
|
|
|
for (int i = 0; i < DSP_IROM_SIZE; i++)
|
|
|
|
g_dsp.irom[i] = Common::swap16(g_dsp.irom[i]);
|
|
|
|
|
2009-04-05 11:55:18 +00:00
|
|
|
return true;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
2009-04-06 18:47:21 +00:00
|
|
|
// Always keep IROM write protected.
|
|
|
|
WriteProtectMemory(g_dsp.irom, DSP_IROM_BYTE_SIZE, false);
|
2009-04-05 11:55:18 +00:00
|
|
|
return false;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
|
|
|
|
2009-04-05 11:55:18 +00:00
|
|
|
bool gdsp_load_coef(const char *fname)
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
2009-04-05 11:55:18 +00:00
|
|
|
FILE *pFile = fopen(fname, "rb");
|
2009-03-23 09:10:32 +00:00
|
|
|
if (pFile)
|
|
|
|
{
|
2009-04-05 11:55:18 +00:00
|
|
|
size_t size_in_bytes = DSP_COEF_SIZE * sizeof(u16);
|
|
|
|
size_t read_bytes = fread(g_dsp.coef, 1, size_in_bytes, pFile);
|
|
|
|
if (read_bytes != size_in_bytes)
|
|
|
|
{
|
|
|
|
PanicAlert("COEF too short : %i/%i", (int)read_bytes, (int)size_in_bytes);
|
|
|
|
fclose(pFile);
|
|
|
|
return false;
|
|
|
|
}
|
2009-03-23 09:10:32 +00:00
|
|
|
fclose(pFile);
|
2009-04-08 21:46:54 +00:00
|
|
|
// Byteswap the rom.
|
|
|
|
for (int i = 0; i < DSP_IROM_SIZE; i++)
|
|
|
|
g_dsp.coef[i] = Common::swap16(g_dsp.coef[i]);
|
2009-04-05 11:55:18 +00:00
|
|
|
return true;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
2009-04-06 18:47:21 +00:00
|
|
|
// Always keep COEF write protected. We unprotect only when DMA-ing
|
|
|
|
WriteProtectMemory(g_dsp.coef, DSP_COEF_BYTE_SIZE, false);
|
2009-04-05 11:55:18 +00:00
|
|
|
return false;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
|
|
|
|
2009-04-05 11:55:18 +00:00
|
|
|
// Hm, should instructions that change CR use this? Probably not (but they
|
|
|
|
// should call UpdateCachedCR())
|
2009-03-30 03:27:31 +00:00
|
|
|
void gdsp_write_cr(u16 val)
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
|
|
|
// reset
|
|
|
|
if (val & 0x0001)
|
|
|
|
{
|
|
|
|
gdsp_reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
val &= ~0x0001;
|
|
|
|
|
|
|
|
// update cr
|
|
|
|
g_dsp.cr = val;
|
|
|
|
|
|
|
|
UpdateCachedCR();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-05 11:55:18 +00:00
|
|
|
// Hm, should instructions that read CR use this? (Probably not).
|
|
|
|
u16 gdsp_read_cr()
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
|
|
|
if (g_dsp.pc & 0x8000)
|
|
|
|
{
|
|
|
|
g_dsp.cr |= 0x800;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_dsp.cr &= ~0x800;
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateCachedCR();
|
|
|
|
|
2009-04-05 11:55:18 +00:00
|
|
|
return g_dsp.cr;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gdsp_step()
|
|
|
|
{
|
|
|
|
g_dsp.step_counter++;
|
|
|
|
|
2009-04-08 21:46:54 +00:00
|
|
|
#if PROFILE
|
2009-03-23 09:10:32 +00:00
|
|
|
g_dsp.err_pc = g_dsp.pc;
|
|
|
|
|
|
|
|
ProfilerAddDelta(g_dsp.err_pc, 1);
|
|
|
|
if (g_dsp.step_counter == 1)
|
|
|
|
{
|
|
|
|
ProfilerInit();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((g_dsp.step_counter & 0xFFFFF) == 0)
|
|
|
|
{
|
|
|
|
ProfilerDump(g_dsp.step_counter);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-03-30 03:27:31 +00:00
|
|
|
u16 opc = dsp_fetch_code();
|
2009-04-08 20:26:33 +00:00
|
|
|
ExecuteInstruction(UDSPInstruction(opc));
|
2009-03-23 09:10:32 +00:00
|
|
|
|
2009-04-08 20:26:33 +00:00
|
|
|
// Handle looping hardware.
|
|
|
|
u16& rLoopCounter = g_dsp.r[DSP_REG_ST3];
|
2009-03-23 09:10:32 +00:00
|
|
|
if (rLoopCounter > 0)
|
|
|
|
{
|
2009-04-08 20:26:33 +00:00
|
|
|
const u16 rCallAddress = g_dsp.r[DSP_REG_ST0];
|
|
|
|
const u16 rLoopAddress = g_dsp.r[DSP_REG_ST2];
|
2009-03-23 09:10:32 +00:00
|
|
|
|
|
|
|
if (g_dsp.pc == (rLoopAddress + 1))
|
|
|
|
{
|
|
|
|
rLoopCounter--;
|
|
|
|
|
|
|
|
if (rLoopCounter > 0)
|
|
|
|
{
|
|
|
|
g_dsp.pc = rCallAddress;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// end of loop
|
|
|
|
dsp_reg_load_stack(0);
|
|
|
|
dsp_reg_load_stack(2);
|
|
|
|
dsp_reg_load_stack(3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if there is an external interrupt
|
2009-04-05 11:55:18 +00:00
|
|
|
if (cr_external_int)
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
|
|
|
if (dsp_SR_is_flag_set(FLAG_ENABLE_INTERUPT) && (g_dsp.exception_in_progress_hack == false))
|
|
|
|
{
|
|
|
|
// level 7 is the interrupt exception
|
|
|
|
gdsp_generate_exception(7);
|
|
|
|
g_dsp.cr &= ~0x0002;
|
|
|
|
UpdateCachedCR();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check exceptions
|
2009-04-05 09:23:43 +00:00
|
|
|
if ((g_dsp.exceptions != 0) && (!g_dsp.exception_in_progress_hack))
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
2009-04-05 09:23:43 +00:00
|
|
|
for (int i = 0; i < 8; i++)
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
2009-04-05 09:23:43 +00:00
|
|
|
if (g_dsp.exceptions & (1 << i))
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
2009-04-05 09:23:43 +00:00
|
|
|
_assert_msg_(MASTER_LOG, !g_dsp.exception_in_progress_hack, "assert while exception");
|
2009-03-23 09:10:32 +00:00
|
|
|
|
|
|
|
dsp_reg_store_stack(DSP_STACK_C, g_dsp.pc);
|
2009-04-06 17:28:14 +00:00
|
|
|
dsp_reg_store_stack(DSP_STACK_D, g_dsp.r[DSP_REG_SR]);
|
2009-03-23 09:10:32 +00:00
|
|
|
|
|
|
|
g_dsp.pc = i * 2;
|
2009-04-05 09:23:43 +00:00
|
|
|
g_dsp.exceptions &= ~(1 << i);
|
2009-03-23 09:10:32 +00:00
|
|
|
|
|
|
|
g_dsp.exception_in_progress_hack = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-08 20:26:33 +00:00
|
|
|
// Used by thread mode.
|
|
|
|
void gdsp_run()
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
|
|
|
gdsp_running = true;
|
2009-04-05 11:55:18 +00:00
|
|
|
while (!cr_halt)
|
2009-03-23 09:10:32 +00:00
|
|
|
{
|
2009-04-08 12:37:15 +00:00
|
|
|
// Are we running?
|
2009-04-08 20:26:33 +00:00
|
|
|
if (*g_dspInitialize.pEmulatorState)
|
2009-04-08 12:37:15 +00:00
|
|
|
break;
|
|
|
|
|
2009-03-23 09:10:32 +00:00
|
|
|
gdsp_step();
|
2009-04-03 12:21:02 +00:00
|
|
|
|
2009-04-05 11:55:18 +00:00
|
|
|
if (!gdsp_running)
|
2009-04-03 12:21:02 +00:00
|
|
|
break;
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
|
|
|
gdsp_running = false;
|
2009-04-08 20:26:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Used by non-thread mode.
|
|
|
|
void gdsp_run_cycles(int cycles)
|
|
|
|
{
|
|
|
|
// First, let's run a few cycles with no idle skipping so that things can progress a bit.
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
if (cr_halt)
|
|
|
|
return;
|
|
|
|
gdsp_step();
|
|
|
|
cycles--;
|
|
|
|
}
|
|
|
|
// Next, let's run a few cycles with idle skipping, so that we can skip loops.
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
if (cr_halt)
|
|
|
|
return;
|
|
|
|
if (DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
|
|
|
|
return;
|
|
|
|
gdsp_step();
|
|
|
|
cycles--;
|
|
|
|
}
|
|
|
|
// Now, run the rest of the block without idle skipping. It might trip into a
|
|
|
|
// idle loop and if so we waste some time here. Might be beneficial to slice even further.
|
|
|
|
while (cycles > 0)
|
|
|
|
{
|
|
|
|
gdsp_step();
|
|
|
|
cycles--;
|
|
|
|
// We don't bother directly supporting pause - if the main emu pauses,
|
|
|
|
// it just won't call this function anymore.
|
|
|
|
}
|
2009-03-23 09:10:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gdsp_stop()
|
|
|
|
{
|
|
|
|
gdsp_running = false;
|
|
|
|
}
|
|
|
|
|