Core and DSPCore now have mutual dependencies, which breaks the

assumption on the part of many linkers that static libraries make
up a DAG, so merge libdspcore into libcore.


git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6967 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Soren Jorvang
2011-01-29 14:06:48 +00:00
parent 10675ba3c0
commit b3a183dffe
78 changed files with 395 additions and 1003 deletions

View File

@ -0,0 +1,176 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "DSPCore.h"
#include "DSPHost.h"
#include "DSPHWInterface.h"
#include "DSPInterpreter.h"
// The hardware adpcm decoder :)
static s16 ADPCM_Step(u32& _rSamplePos)
{
const s16 *pCoefTable = (const s16 *)&g_dsp.ifx_regs[DSP_COEF_A1_0];
if (((_rSamplePos) & 15) == 0)
{
g_dsp.ifx_regs[DSP_PRED_SCALE] = DSPHost_ReadHostMemory((_rSamplePos & ~15) >> 1);
_rSamplePos += 2;
}
int scale = 1 << (g_dsp.ifx_regs[DSP_PRED_SCALE] & 0xF);
int coef_idx = (g_dsp.ifx_regs[DSP_PRED_SCALE] >> 4) & 0x7;
s32 coef1 = pCoefTable[coef_idx * 2 + 0];
s32 coef2 = pCoefTable[coef_idx * 2 + 1];
int temp = (_rSamplePos & 1) ?
(DSPHost_ReadHostMemory(_rSamplePos >> 1) & 0xF) :
(DSPHost_ReadHostMemory(_rSamplePos >> 1) >> 4);
if (temp >= 8)
temp -= 16;
// 0x400 = 0.5 in 11-bit fixed point
int val = (scale * temp) + ((0x400 + coef1 * (s16)g_dsp.ifx_regs[DSP_YN1] + coef2 * (s16)g_dsp.ifx_regs[DSP_YN2]) >> 11);
// Clamp values.
if (val > 0x7FFF)
val = 0x7FFF;
else if (val < -0x7FFF)
val = -0x7FFF;
g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
g_dsp.ifx_regs[DSP_YN1] = val;
_rSamplePos++;
// The advanced interpolation (linear, polyphase,...) is done by the UCode,
// so we don't need to bother with it here.
return val;
}
u16 dsp_read_aram_d3()
{
// Zelda ucode reads ARAM through 0xffd3.
const u32 EndAddress = (g_dsp.ifx_regs[DSP_ACEAH] << 16) | g_dsp.ifx_regs[DSP_ACEAL];
u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL];
u16 val = 0;
switch (g_dsp.ifx_regs[DSP_FORMAT]) {
case 0x5: // u8 reads
val = DSPHost_ReadHostMemory(Address);
Address++;
break;
case 0x6: // u16 reads
val = (DSPHost_ReadHostMemory(Address*2) << 8) | DSPHost_ReadHostMemory(Address*2 + 1);
Address++;
break;
default:
ERROR_LOG(DSPLLE, "dsp_read_aram_d3() - unknown format 0x%x", g_dsp.ifx_regs[DSP_FORMAT]);
break;
}
if (Address >= EndAddress)
{
// Set address back to start address. (never seen this here!)
Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL];
}
g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16;
g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff;
return val;
}
void dsp_write_aram_d3(u16 value)
{
// Zelda ucode writes a bunch of zeros to ARAM through d3 during
// initialization. Don't know if it ever does it later, too.
u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL];
switch (g_dsp.ifx_regs[DSP_FORMAT]) {
case 0xA: // u16 writes
DSPHost_WriteHostMemory(value >> 8, Address*2);
DSPHost_WriteHostMemory(value & 0xFF, Address*2 + 1);
Address++;
break;
default:
ERROR_LOG(DSPLLE, "dsp_write_aram_d3() - unknown format 0x%x", g_dsp.ifx_regs[DSP_FORMAT]);
break;
}
g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16;
g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff;
}
u16 dsp_read_accelerator()
{
const u32 EndAddress = (g_dsp.ifx_regs[DSP_ACEAH] << 16) | g_dsp.ifx_regs[DSP_ACEAL];
u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL];
u16 val;
// let's do the "hardware" decode DSP_FORMAT is interesting - the Zelda
// ucode seems to indicate that the bottom two bits specify the "read size"
// and the address multiplier. The bits above that may be things like sign
// extention and do/do not use ADPCM. It also remains to be figured out
// whether there's a difference between the usual accelerator "read
// address" and 0xd3.
switch (g_dsp.ifx_regs[DSP_FORMAT])
{
case 0x00: // ADPCM audio
val = ADPCM_Step(Address);
break;
case 0x0A: // 16-bit PCM audio
val = (DSPHost_ReadHostMemory(Address*2) << 8) | DSPHost_ReadHostMemory(Address*2 + 1);
g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
g_dsp.ifx_regs[DSP_YN1] = val;
Address++;
break;
case 0x19: // 8-bit PCM audio
val = DSPHost_ReadHostMemory(Address) << 8;
g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
g_dsp.ifx_regs[DSP_YN1] = val;
Address++;
break;
default:
ERROR_LOG(DSPLLE, "dsp_read_accelerator() - unknown format 0x%x", g_dsp.ifx_regs[DSP_FORMAT]);
Address++;
val = 0;
break;
}
// TODO: Take GAIN into account
// adpcm = 0, pcm8 = 0x100, pcm16 = 0x800
// games using pcm8 : Phoenix Wright Ace Attorney (Wiiware), Megaman 9-10 (WiiWare)
// games using pcm16: gc sega games, ...
// Check for loop.
// Somehow, YN1 and YN2 must be initialized with their "loop" values,
// so yeah, it seems likely that we should raise an exception to let
// the DSP program do that, at least if DSP_FORMAT == 0x0A.
if (Address >= EndAddress)
{
// Set address back to start address.
Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL];
DSPCore_SetException(EXP_ACCOV);
}
g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16;
g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff;
return val;
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _DSP_ACCELERATOR_H
#define _DSP_ACCELERATOR_H
u16 dsp_read_accelerator();
u16 dsp_read_aram_d3();
void dsp_write_aram_d3(u16 value);
#endif

View File

@ -0,0 +1,173 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "DSPAnalyzer.h"
#include "DSPInterpreter.h"
#include "DSPTables.h"
#include "DSPMemoryMap.h"
namespace DSPAnalyzer {
// Holds data about all instructions in RAM.
u8 code_flags[ISPACE];
// Good candidates for idle skipping is mail wait loops. If we're time slicing
// between the main CPU and the DSP, if the DSP runs into one of these, it might
// as well give up its time slice immediately, after executing once.
// Max signature length is 6. A 0 in a signature is ignored.
#define NUM_IDLE_SIGS 8
#define MAX_IDLE_SIG_SIZE 6
// 0xFFFF means ignore.
const u16 idle_skip_sigs[NUM_IDLE_SIGS][MAX_IDLE_SIG_SIZE + 1] =
{
// From AX:
{ 0x26fc, // LRS $30, @DMBH
0x02c0, 0x8000, // ANDCF $30, #0x8000
0x029d, 0xFFFF, // JLZ 0x027a
0, 0 }, // RET
{ 0x27fc, // LRS $31, @DMBH
0x03c0, 0x8000, // ANDCF $31, #0x8000
0x029d, 0xFFFF, // JLZ 0x027a
0, 0 }, // RET
{ 0x26fe, // LRS $30, @CMBH
0x02c0, 0x8000, // ANDCF $30, #0x8000
0x029c, 0xFFFF, // JLNZ 0x0280
0, 0 }, // RET
{ 0x27fe, // LRS $31, @CMBH
0x03c0, 0x8000, // ANDCF $31, #0x8000
0x029c, 0xFFFF, // JLNZ 0x0280
0, 0 }, // RET
{ 0x26fc, // lrs $AC0.M, @DMBH
0x02a0, 0x8000, // andf $AC0.M, #0x8000
0x029c, 0xFFFF, // jlnz 0x????
0, 0 },
{ 0x27fc, // lrs $AC1.M, @DMBH
0x03a0, 0x8000, // andf $AC1.M, #0x8000
0x029c, 0xFFFF, // jlnz 0x????
0, 0 },
// From Zelda:
{ 0x00de, 0xFFFE, // LR $AC0.M, @CMBH
0x02c0, 0x8000, // ANDCF $AC0.M, #0x8000
0x029c, 0xFFFF, // JLNZ 0x05cf
0 },
// From Zelda - experimental
{ 0x00da, 0x0352, // lr $AX0.H, @0x0352
0x8600, // tstaxh $AX0.H
0x0295, 0xFFFF, // jz 0x????
0, 0 }
};
void Reset()
{
memset(code_flags, 0, sizeof(code_flags));
}
void AnalyzeRange(int start_addr, int end_addr)
{
// First we run an extremely simplified version of a disassembler to find
// where all instructions start.
// This may not be 100% accurate in case of jump tables!
// It could get desynced, which would be bad. We'll see if that's an issue.
u16 last_arithmetic = 0;
for (int addr = start_addr; addr < end_addr;)
{
UDSPInstruction inst = dsp_imem_read(addr);
const DSPOPCTemplate *opcode = GetOpTemplate(inst);
if (!opcode)
{
addr++;
continue;
}
code_flags[addr] |= CODE_START_OF_INST;
// Look for loops.
if ((inst & 0xffe0) == 0x0060 || (inst & 0xff00) == 0x1100)
{
// BLOOP, BLOOPI
u16 loop_end = dsp_imem_read(addr + 1);
code_flags[addr] |= CODE_LOOP_START;
code_flags[loop_end] |= CODE_LOOP_END;
}
else if ((inst & 0xffe0) == 0x0040 || (inst & 0xff00) == 0x1000)
{
// LOOP, LOOPI
code_flags[addr] |= CODE_LOOP_START;
code_flags[addr + 1] |= CODE_LOOP_END;
}
// Mark the last arithmetic/multiplier instruction before a branch.
// We must update the SR reg at these instructions
if (opcode->updates_sr)
{
last_arithmetic = addr;
}
if (opcode->branch && !opcode->uncond_branch)
{
code_flags[last_arithmetic] |= CODE_UPDATE_SR;
}
// If an instruction potentially raises exceptions, mark the following
// instruction as needing to check for exceptions
if (opcode->opcode == 0x00c0 ||
opcode->opcode == 0x1800 ||
opcode->opcode == 0x1880 ||
opcode->opcode == 0x1900 ||
opcode->opcode == 0x1980 ||
opcode->opcode == 0x2000 ||
opcode->extended
)
code_flags[addr + opcode->size] |= CODE_CHECK_INT;
addr += opcode->size;
}
// Next, we'll scan for potential idle skips.
for (int s = 0; s < NUM_IDLE_SIGS; s++)
{
for (int addr = start_addr; addr < end_addr; addr++)
{
bool found = false;
for (int i = 0; i < MAX_IDLE_SIG_SIZE + 1; i++)
{
if (idle_skip_sigs[s][i] == 0)
found = true;
if (idle_skip_sigs[s][i] == 0xFFFF)
continue;
if (idle_skip_sigs[s][i] != dsp_imem_read(addr + i))
break;
}
if (found)
{
INFO_LOG(DSPLLE, "Idle skip location found at %02x (sigNum:%d)", addr, s+1);
code_flags[addr] |= CODE_IDLE_SKIP;
}
}
}
INFO_LOG(DSPLLE, "Finished analysis.");
}
void Analyze()
{
Reset();
AnalyzeRange(0x0000, 0x1000); // IRAM
AnalyzeRange(0x8000, 0x9000); // IROM
}
} // namespace

View File

@ -0,0 +1,52 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Basic code analysis.
#include "DSPInterpreter.h"
namespace DSPAnalyzer {
#define ISPACE 65536
// Useful things to detect:
// * Loop endpoints - so that we can avoid checking for loops every cycle.
enum
{
CODE_START_OF_INST = 1,
CODE_IDLE_SKIP = 2,
CODE_LOOP_START = 4,
CODE_LOOP_END = 8,
CODE_UPDATE_SR = 16,
CODE_CHECK_INT = 32,
};
// Easy to query array covering the whole of instruction memory.
// Just index by address.
// This one will be helpful for debuggers and jits.
extern u8 code_flags[ISPACE];
// This one should be called every time IRAM changes - which is basically
// every time that a new ucode gets uploaded, and never else. At that point,
// we can do as much static analysis as we want - but we should always throw
// all old analysis away. Luckily the entire address space is only 64K code
// words and the actual code space 8K instructions in total, so we can do
// some pretty expensive analysis if necessary.
void Analyze();
} // namespace

View File

@ -0,0 +1,63 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _DSP_BREAKPOINTS
#define _DSP_BREAKPOINTS
#include "Common.h"
// super fast breakpoints for a limited range.
// To be used interchangably with the BreakPoints class.
class DSPBreakpoints
{
public:
DSPBreakpoints() {Clear();}
// is address breakpoint
bool IsAddressBreakPoint(u32 addr) {
return b[addr] != 0;
}
// AddBreakPoint
bool Add(u32 addr, bool temp=false) {
bool was_one = b[addr] != 0;
if (!was_one) {
b[addr] = temp ? 2 : 1;
return true;
} else {
return false;
}
}
// Remove Breakpoint
bool Remove(u32 addr) {
bool was_one = b[addr] != 0;
b[addr] = 0;
return was_one;
}
void Clear() {
for (int i = 0; i < 65536; i++)
b[i] = 0;
}
void DeleteByAddress(u32 addr) {
b[addr] = 0;
}
private:
u8 b[65536];
};
#endif

View File

@ -0,0 +1,233 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include <iostream>
#include <vector>
#include "Common.h"
#include "FileUtil.h"
#include "StringUtil.h"
#include "DSPCodeUtil.h"
#include "assemble.h"
#include "disassemble.h"
bool Assemble(const char *text, std::vector<u16> &code, bool force)
{
AssemblerSettings settings;
// settings.pc = 0;
// settings.decode_registers = false;
// settings.decode_names = false;
settings.force = force;
// settings.print_tabs = false;
// settings.ext_separator = '\'';
// TODO: fix the terrible api of the assembler.
DSPAssembler assembler(settings);
if (!assembler.Assemble(text, code)) {
std::cerr << assembler.GetErrorString() << std::endl;
return false;
}
return true;
}
bool Disassemble(const std::vector<u16> &code, bool line_numbers, std::string &text)
{
if (code.empty())
return false;
AssemblerSettings settings;
// These two prevent roundtripping.
settings.show_hex = true;
settings.show_pc = line_numbers;
settings.ext_separator = '\'';
settings.decode_names = true;
settings.decode_registers = true;
DSPDisassembler disasm(settings);
bool success = disasm.Disassemble(0, code, 0x0000, text);
return success;
}
bool Compare(const std::vector<u16> &code1, const std::vector<u16> &code2)
{
if (code1.size() != code2.size())
printf("Size difference! 1=%i 2=%i\n", (int)code1.size(), (int)code2.size());
u32 count_equal = 0;
const int min_size = (int)std::min(code1.size(), code2.size());
AssemblerSettings settings;
DSPDisassembler disassembler(settings);
for (int i = 0; i < min_size; i++)
{
if (code1[i] == code2[i])
count_equal++;
else
{
std::string line1, line2;
u16 pc = i;
disassembler.DisOpcode(&code1[0], 0x0000, 2, &pc, line1);
pc = i;
disassembler.DisOpcode(&code2[0], 0x0000, 2, &pc, line2);
printf("!! %04x : %04x vs %04x - %s vs %s\n", i, code1[i], code2[i], line1.c_str(), line2.c_str());
}
}
if (code2.size() != code1.size())
{
printf("Extra code words:\n");
const std::vector<u16> &longest = code1.size() > code2.size() ? code1 : code2;
for (int i = min_size; i < (int)longest.size(); i++)
{
u16 pc = i;
std::string line;
disassembler.DisOpcode(&longest[0], 0x0000, 2, &pc, line);
printf("!! %s\n", line.c_str());
}
}
printf("Equal instruction words: %i / %i\n", count_equal, min_size);
return code1.size() == code2.size() && code1.size() == count_equal;
}
void GenRandomCode(int size, std::vector<u16> &code)
{
code.resize(size);
for (int i = 0; i < size; i++)
{
code[i] = rand() ^ (rand() << 8);
}
}
void CodeToHeader(const std::vector<u16> &code, std::string _filename,
const char *name, std::string &header)
{
std::vector<u16> code_padded = code;
// Pad with nops to 32byte boundary
while (code_padded.size() & 0x7f)
code_padded.push_back(0);
char buffer[1024];
header.clear();
header.reserve(code_padded.size() * 4);
header.append("#define NUM_UCODES 1\n\n");
std::string filename;
SplitPath(_filename, NULL, &filename, NULL);
header.append(StringFromFormat("const char* UCODE_NAMES[NUM_UCODES] = {\"%s\"};\n\n", filename.c_str()));
header.append("const unsigned short dsp_code[NUM_UCODES][0x1000] = {\n");
header.append("\t{\n\t\t");
for (u32 j = 0; j < code_padded.size(); j++)
{
if (j && ((j & 15) == 0))
header.append("\n\t\t");
sprintf(buffer, "0x%04x, ", code_padded[j]);
header.append(buffer);
}
header.append("\n\t},\n");
header.append("};\n");
}
void CodesToHeader(const std::vector<u16> *codes, const std::vector<std::string>* filenames,
int numCodes, const char *name, std::string &header)
{
std::vector<std::vector<u16> > codes_padded;
char buffer[1024];
int reserveSize = 0;
for(int i = 0; i < numCodes; i++)
{
codes_padded.push_back(codes[i]);
// Pad with nops to 32byte boundary
while (codes_padded.at(i).size() & 0x7f)
codes_padded.at(i).push_back(0);
reserveSize += (int)codes_padded.at(i).size();
}
header.clear();
header.reserve(reserveSize * 4);
sprintf(buffer, "#define NUM_UCODES %d\n\n", numCodes);
header.append(buffer);
header.append("const char* UCODE_NAMES[NUM_UCODES] = {\n");
for (int i = 0; i < numCodes; i++)
{
std::string filename;
if (! SplitPath(filenames->at(i), NULL, &filename, NULL))
filename = filenames->at(i);
sprintf(buffer, "\t\"%s\",\n", filename.c_str());
header.append(buffer);
}
header.append("};\n\n");
header.append("const unsigned short dsp_code[NUM_UCODES][0x1000] = {\n");
for(int i = 0; i < numCodes; i++)
{
if(codes[i].size() == 0)
continue;
header.append("\t{\n\t\t");
for (u32 j = 0; j < codes_padded.at(i).size(); j++)
{
if (j && ((j & 15) == 0))
header.append("\n\t\t");
sprintf(buffer, "0x%04x, ", codes_padded.at(i).at(j));
header.append(buffer);
}
header.append("\n\t},\n");
}
header.append("};\n");
}
void CodeToBinaryStringBE(const std::vector<u16> &code, std::string &str)
{
str.resize(code.size() * 2);
for (int i = 0; i < (int)code.size(); i++)
{
str[i * 2 + 0] = code[i] >> 8;
str[i * 2 + 1] = code[i] & 0xff;
}
}
void BinaryStringBEToCode(const std::string &str, std::vector<u16> &code)
{
code.resize(str.size() / 2);
for (int i = 0; i < (int)code.size(); i++)
{
code[i] = ((u16)(u8)str[i * 2 + 0] << 8) | ((u16)(u8)str[i * 2 + 1]);
}
}
bool LoadBinary(const char *filename, std::vector<u16> &code)
{
std::string buffer;
if (!File::ReadFileToString(false, filename, buffer))
return false;
BinaryStringBEToCode(buffer, code);
return true;
}
bool SaveBinary(const std::vector<u16> &code, const char *filename)
{
std::string buffer;
CodeToBinaryStringBE(code, buffer);
if (!File::WriteStringToFile(false, buffer, filename))
return false;
return true;
}

View File

@ -0,0 +1,43 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _DSPCODEUTIL_H
#define _DSPCODEUTIL_H
#include <string>
#include <vector>
#include "Common.h"
bool Assemble(const char *text, std::vector<u16> &code, bool force = false);
bool Disassemble(const std::vector<u16> &code, bool line_numbers, std::string &text);
bool Compare(const std::vector<u16> &code1, const std::vector<u16> &code2);
void GenRandomCode(int size, std::vector<u16> &code);
void CodeToHeader(const std::vector<u16> &code, std::string _filename,
const char *name, std::string &header);
void CodesToHeader(const std::vector<u16> *codes, const std::vector<std::string> *filenames,
int numCodes, const char *name, std::string &header);
// Big-endian, for writing straight to file using File::WriteStringToFile.
void CodeToBinaryStringBE(const std::vector<u16> &code, std::string &str);
void BinaryStringBEToCode(const std::string &str, std::vector<u16> &code);
// Load code (big endian binary).
bool LoadBinary(const char *filename, std::vector<u16> &code);
bool SaveBinary(const std::vector<u16> &code, const char *filename);
#endif // _DSPCODEUTIL_H

View File

@ -0,0 +1,8 @@
#ifndef __DSPCOMMON_H
#define __DSPCOMMON_H
#include "CommonTypes.h"
typedef u16 UDSPInstruction;
#endif

View File

@ -0,0 +1,443 @@
/*====================================================================
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 "Common.h"
#include "Hash.h"
#include "Thread.h"
#include "DSPCore.h"
#include "DSPEmitter.h"
#include "DSPHost.h"
#include "DSPAnalyzer.h"
#include "MemoryUtil.h"
#include "DSPHWInterface.h"
#include "DSPIntUtil.h"
SDSP g_dsp;
DSPBreakpoints dsp_breakpoints;
DSPCoreState core_state = DSPCORE_STOP;
u16 cyclesLeft = 0;
DSPEmitter *dspjit = NULL;
Common::Event step_event;
Common::CriticalSection ExtIntCriticalSection;
static bool LoadRom(const char *fname, int size_in_words, u16 *rom)
{
FILE *pFile = fopen(fname, "rb");
const size_t size_in_bytes = size_in_words * sizeof(u16);
if (pFile)
{
fread(rom, 1, size_in_bytes, pFile);
fclose(pFile);
// Byteswap the rom.
for (int i = 0; i < size_in_words; i++)
rom[i] = Common::swap16(rom[i]);
// Always keep ROMs write protected.
WriteProtectMemory(rom, size_in_bytes, false);
return true;
}
PanicAlertT("Failed to load DSP ROM: %s", fname);
return false;
}
// Returns false iff the hash fails and the user hits "Yes"
static bool VerifyRoms(const char *irom_filename, const char *coef_filename)
{
static const u32 hash[] = { 0x66f334fe, 0xf3b93527 };
static const int size[] = { DSP_IROM_BYTE_SIZE, DSP_COEF_BYTE_SIZE };
const u16 *data[] = { g_dsp.irom, g_dsp.coef };
for (int i = 0; i < 2; i++)
{
if (HashAdler32((u8*)data[i], size[i]) != hash[i])
{
if (AskYesNoT("%s has an incorrect hash.\n"
"Would you like to stop now to fix the problem?\n"
"If you select \"No\", audio will be garbled.",
(i == 0) ? irom_filename : coef_filename))
return false;
}
}
return true;
}
bool DSPCore_Init(const char *irom_filename, const char *coef_filename,
bool bUsingJIT)
{
g_dsp.step_counter = 0;
cyclesLeft = 0;
dspjit = NULL;
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);
// Fill roms with zeros.
memset(g_dsp.irom, 0, DSP_IROM_BYTE_SIZE);
memset(g_dsp.coef, 0, DSP_COEF_BYTE_SIZE);
// Try to load real ROM contents.
LoadRom(irom_filename, DSP_IROM_SIZE, g_dsp.irom);
LoadRom(coef_filename, DSP_COEF_SIZE, g_dsp.coef);
if (!VerifyRoms(irom_filename, coef_filename))
return false;
memset(&g_dsp.r,0,sizeof(g_dsp.r));
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;
}
}
// Fill IRAM with HALT opcodes.
for (int i = 0; i < DSP_IRAM_SIZE; i++)
{
g_dsp.iram[i] = 0x0021; // HALT opcode
}
// Just zero out DRAM.
for (int i = 0; i < DSP_DRAM_SIZE; i++)
{
g_dsp.dram[i] = 0;
}
// Copied from a real console after the custom UCode has been loaded.
// These are the indexing wrapping registers.
g_dsp.r.wr[0] = 0xffff;
g_dsp.r.wr[1] = 0xffff;
g_dsp.r.wr[2] = 0xffff;
g_dsp.r.wr[3] = 0xffff;
g_dsp.r.sr |= SR_INT_ENABLE;
g_dsp.r.sr |= SR_EXT_INT_ENABLE;
g_dsp.cr = 0x804;
gdsp_ifx_init();
// Mostly keep IRAM write protected. We unprotect only when DMA-ing
// in new ucodes.
WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
// Initialize JIT, if necessary
if(bUsingJIT)
dspjit = new DSPEmitter();
DSPAnalyzer::Analyze();
step_event.Init();
core_state = DSPCORE_RUNNING;
return true;
}
void DSPCore_Shutdown()
{
core_state = DSPCORE_STOP;
if(dspjit) {
delete dspjit;
dspjit = NULL;
}
step_event.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);
}
void DSPCore_Reset()
{
g_dsp.pc = DSP_RESET_VECTOR;
g_dsp.r.wr[0] = 0xffff;
g_dsp.r.wr[1] = 0xffff;
g_dsp.r.wr[2] = 0xffff;
g_dsp.r.wr[3] = 0xffff;
}
void DSPCore_SetException(u8 level)
{
g_dsp.exceptions |= 1 << level;
}
// Notify that an external interrupt is pending (used by thread mode)
void DSPCore_SetExternalInterrupt(bool val)
{
ExtIntCriticalSection.Enter();
g_dsp.external_interrupt_waiting = val;
ExtIntCriticalSection.Leave();
}
// Coming from the CPU
void DSPCore_CheckExternalInterrupt()
{
if (! dsp_SR_is_flag_set(SR_EXT_INT_ENABLE))
return;
// Signal the SPU about new mail
DSPCore_SetException(EXP_INT);
g_dsp.cr &= ~CR_EXTERNAL_INT;
}
void DSPCore_CheckExceptions()
{
// Early out to skip the loop in the common case.
if (g_dsp.exceptions == 0)
return;
for (int i = 7; i > 0; i--) {
// Seems exp int are not masked by sr_int_enable
if (g_dsp.exceptions & (1 << i)) {
if (dsp_SR_is_flag_set(SR_INT_ENABLE) || (i == EXP_INT)) {
// store pc and sr until RTI
dsp_reg_store_stack(DSP_STACK_C, g_dsp.pc);
dsp_reg_store_stack(DSP_STACK_D, g_dsp.r.sr);
g_dsp.pc = i * 2;
g_dsp.exceptions &= ~(1 << i);
if (i == 7)
g_dsp.r.sr &= ~SR_EXT_INT_ENABLE;
else
g_dsp.r.sr &= ~SR_INT_ENABLE;
break;
} else {
#if defined(_DEBUG) || defined(DEBUGFAST)
ERROR_LOG(DSPLLE, "Firing exception %d failed", i);
#endif
}
}
}
}
// Delegate to JIT or interpreter as appropriate.
// Handle state changes and stepping.
int DSPCore_RunCycles(int cycles)
{
if (dspjit)
{
cyclesLeft = cycles;
CompiledCode pExecAddr = (CompiledCode)dspjit->enterDispatcher;
pExecAddr();
if (g_dsp.external_interrupt_waiting)
{
DSPCore_CheckExternalInterrupt();
DSPCore_CheckExceptions();
DSPCore_SetExternalInterrupt(false);
}
return cyclesLeft;
}
while (cycles > 0)
{
reswitch:
switch (core_state)
{
case DSPCORE_RUNNING:
// Seems to slow things down
#if defined(_DEBUG) || defined(DEBUGFAST)
cycles = DSPInterpreter::RunCyclesDebug(cycles);
#else
cycles = DSPInterpreter::RunCycles(cycles);
#endif
break;
case DSPCORE_STEPPING:
step_event.Wait();
if (core_state != DSPCORE_STEPPING)
goto reswitch;
DSPInterpreter::Step();
cycles--;
DSPHost_UpdateDebugger();
break;
case DSPCORE_STOP:
break;
}
}
return cycles;
}
void DSPCore_SetState(DSPCoreState new_state)
{
core_state = new_state;
// kick the event, in case we are waiting
if (new_state == DSPCORE_RUNNING)
step_event.Set();
// Sleep(10);
DSPHost_UpdateDebugger();
}
DSPCoreState DSPCore_GetState()
{
return core_state;
}
void DSPCore_Step()
{
if (core_state == DSPCORE_STEPPING)
step_event.Set();
}
void CompileCurrent()
{
dspjit->Compile(g_dsp.pc);
bool retry = true;
while (retry)
{
retry = false;
for(u16 i = 0x0000; i < 0xffff; ++i)
{
if (!dspjit->unresolvedJumps[i].empty())
{
u16 addrToCompile = dspjit->unresolvedJumps[i].front();
dspjit->Compile(addrToCompile);
if (!dspjit->unresolvedJumps[i].empty())
retry = true;
}
}
}
}
u16 DSPCore_ReadRegister(int reg) {
switch(reg) {
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
return g_dsp.r.ar[reg - DSP_REG_AR0];
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
return g_dsp.r.ix[reg - DSP_REG_IX0];
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
return g_dsp.r.wr[reg - DSP_REG_WR0];
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
return g_dsp.r.st[reg - DSP_REG_ST0];
case DSP_REG_ACH0:
case DSP_REG_ACH1:
return g_dsp.r.ac[reg - DSP_REG_ACH0].h;
case DSP_REG_CR: return g_dsp.r.cr;
case DSP_REG_SR: return g_dsp.r.sr;
case DSP_REG_PRODL: return g_dsp.r.prod.l;
case DSP_REG_PRODM: return g_dsp.r.prod.m;
case DSP_REG_PRODH: return g_dsp.r.prod.h;
case DSP_REG_PRODM2: return g_dsp.r.prod.m2;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
return g_dsp.r.ax[reg - DSP_REG_AXL0].l;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
return g_dsp.r.ax[reg - DSP_REG_AXH0].h;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
return g_dsp.r.ac[reg - DSP_REG_ACL0].l;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
return g_dsp.r.ac[reg - DSP_REG_ACM0].m;
default:
_assert_msg_(DSP_CORE, 0, "cannot happen");
return 0;
}
}
void DSPCore_WriteRegister(int reg, u16 val) {
switch(reg) {
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
g_dsp.r.ar[reg - DSP_REG_AR0] = val;
break;
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
g_dsp.r.ix[reg - DSP_REG_IX0] = val;
break;
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
g_dsp.r.wr[reg - DSP_REG_WR0] = val;
break;
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
g_dsp.r.st[reg - DSP_REG_ST0] = val;
break;
case DSP_REG_ACH0:
case DSP_REG_ACH1:
g_dsp.r.ac[reg - DSP_REG_ACH0].h = val;
break;
case DSP_REG_CR: g_dsp.r.cr = val; break;
case DSP_REG_SR: g_dsp.r.sr = val; break;
case DSP_REG_PRODL: g_dsp.r.prod.l = val; break;
case DSP_REG_PRODM: g_dsp.r.prod.m = val; break;
case DSP_REG_PRODH: g_dsp.r.prod.h = val; break;
case DSP_REG_PRODM2: g_dsp.r.prod.m2 = val; break;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
g_dsp.r.ax[reg - DSP_REG_AXL0].l = val;
break;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
g_dsp.r.ax[reg - DSP_REG_AXH0].h = val;
break;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
g_dsp.r.ac[reg - DSP_REG_ACL0].l = val;
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
g_dsp.r.ac[reg - DSP_REG_ACM0].m = val;
break;
}
}

View File

@ -0,0 +1,306 @@
/*====================================================================
filename: DSPCore.h
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.
====================================================================*/
#ifndef _DSPCORE_H
#define _DSPCORE_H
#include "Thread.h"
#include "DSPBreakpoints.h"
#include "DSPEmitter.h"
#define DSP_IRAM_BYTE_SIZE 0x2000
#define DSP_IRAM_SIZE 0x1000
#define DSP_IRAM_MASK 0x0fff
#define DSP_IROM_BYTE_SIZE 0x2000
#define DSP_IROM_SIZE 0x1000
#define DSP_IROM_MASK 0x0fff
#define DSP_DRAM_BYTE_SIZE 0x2000
#define DSP_DRAM_SIZE 0x1000
#define DSP_DRAM_MASK 0x0fff
#define DSP_COEF_BYTE_SIZE 0x1000
#define DSP_COEF_SIZE 0x800
#define DSP_COEF_MASK 0x7ff
#define DSP_RESET_VECTOR 0x8000
#define DSP_STACK_DEPTH 0x20
#define DSP_STACK_MASK 0x1f
#define DSP_CR_IMEM 2
#define DSP_CR_DMEM 0
#define DSP_CR_TO_CPU 1
#define DSP_CR_FROM_CPU 0
// Register table taken from libasnd
#define DSP_REG_AR0 0x00 // address registers
#define DSP_REG_AR1 0x01
#define DSP_REG_AR2 0x02
#define DSP_REG_AR3 0x03
#define DSP_REG_IX0 0x04 // indexing registers (actually, mostly used as increments)
#define DSP_REG_IX1 0x05
#define DSP_REG_IX2 0x06
#define DSP_REG_IX3 0x07
#define DSP_REG_WR0 0x08 // address wrapping registers. should be initialized to 0xFFFF if not used.
#define DSP_REG_WR1 0x09
#define DSP_REG_WR2 0x0a
#define DSP_REG_WR3 0x0b
#define DSP_REG_ST0 0x0c // stacks.
#define DSP_REG_ST1 0x0d
#define DSP_REG_ST2 0x0e
#define DSP_REG_ST3 0x0f
#define DSP_REG_CR 0x12 // Seems to be the top 8 bits of LRS/SRS.
#define DSP_REG_SR 0x13
#define DSP_REG_PRODL 0x14 // product.
#define DSP_REG_PRODM 0x15
#define DSP_REG_PRODH 0x16
#define DSP_REG_PRODM2 0x17
#define DSP_REG_AXL0 0x18
#define DSP_REG_AXL1 0x19
#define DSP_REG_AXH0 0x1a
#define DSP_REG_AXH1 0x1b
#define DSP_REG_ACC0 0x1c // accumulator (global)
#define DSP_REG_ACC1 0x1d
#define DSP_REG_ACL0 0x1c // Low accumulator
#define DSP_REG_ACL1 0x1d
#define DSP_REG_ACM0 0x1e // Mid accumulator
#define DSP_REG_ACM1 0x1f
#define DSP_REG_ACH0 0x10 // Sign extended 8 bit register 0
#define DSP_REG_ACH1 0x11 // Sign extended 8 bit register 1
// Hardware registers address
#define DSP_COEF_A1_0 0xa0
#define DSP_DSCR 0xc9 // DSP DMA Control Reg
#define DSP_DSPA 0xcd // DSP DMA Address (DSP)
#define DSP_DSBL 0xcb // DSP DMA Block Length
#define DSP_DSMAH 0xce // DSP DMA Address High (External)
#define DSP_DSMAL 0xcf // DSP DMA Address Low (External)
#define DSP_FORMAT 0xd1 // Sample format
#define DSP_ACUNK 0xd2 // Set to 3 on my dumps
#define DSP_ACDATA1 0xd3 // used only by Zelda ucodes
#define DSP_ACSAH 0xd4 // Start of loop
#define DSP_ACSAL 0xd5
#define DSP_ACEAH 0xd6 // End of sample (and loop)
#define DSP_ACEAL 0xd7
#define DSP_ACCAH 0xd8 // Current playback position
#define DSP_ACCAL 0xd9
#define DSP_PRED_SCALE 0xda // ADPCM predictor and scale
#define DSP_YN1 0xdb
#define DSP_YN2 0xdc
#define DSP_ACCELERATOR 0xdd // ADPCM accelerator read. Used by AX.
#define DSP_GAIN 0xde
#define DSP_ACUNK2 0xdf // Set to 0xc on my dumps
#define DSP_AMDM 0xef // ARAM DMA Request Mask 0: DMA with ARAM unmasked 1: masked
#define DSP_DIRQ 0xfb // DSP Irq Rest
#define DSP_DMBH 0xfc // DSP Mailbox H
#define DSP_DMBL 0xfd // DSP Mailbox L
#define DSP_CMBH 0xfe // CPU Mailbox H
#define DSP_CMBL 0xff // CPU Mailbox L
#define DMA_TO_DSP 0
#define DMA_TO_CPU 1
// Stacks
#define DSP_STACK_C 0
#define DSP_STACK_D 1
// cr (Not g_dsp.r[CR]) bits
// See HW/DSP.cpp.
#define CR_EXTERNAL_INT 0x0002
#define CR_HALT 0x0004
#define CR_INIT 0x0400
// SR bits
#define SR_CARRY 0x0001
#define SR_OVERFLOW 0x0002
#define SR_ARITH_ZERO 0x0004
#define SR_SIGN 0x0008
#define SR_OVER_S32 0x0010 // set when there there was mod/tst/cmp on accu and result is over s32
#define SR_TOP2BITS 0x0020 // if the upper (ac?.m/ax?.h) 2 bits are equal
#define SR_LOGIC_ZERO 0x0040
#define SR_OVERFLOW_STICKY 0x0080 // set at the same time as 0x2 (under same conditions) - but not cleared the same
#define SR_100 0x0100 // unknown
#define SR_INT_ENABLE 0x0200 // Not 100% sure but duddie says so. This should replace the hack, if so.
#define SR_400 0x0400 // unknown
#define SR_EXT_INT_ENABLE 0x0800 // Appears in zelda - seems to disable external interupts
#define SR_1000 0x1000 // unknown
#define SR_MUL_MODIFY 0x2000 // 1 = normal. 0 = x2 (M0, M2)
#define SR_40_MODE_BIT 0x4000 // 0 = "16", 1 = "40" (SET16, SET40) Controls sign extension when loading mid accums.
#define SR_MUL_UNSIGNED 0x8000 // 0 = normal. 1 = unsigned (CLR15, SET15) If set, treats operands as unsigned. Tested with mulx only so far.
// This should be the bits affected by CMP. Does not include logic zero.
#define SR_CMP_MASK 0x3f
// exceptions vector
#define EXP_STOVF 1 // 0x0002 stack under/over flow
#define EXP_2 2 // 0x0004
#define EXP_3 3 // 0x0006
#define EXP_4 4 // 0x0008
#define EXP_ACCOV 5 // 0x000a accelerator address overflow
#define EXP_6 6 // 0x000c
#define EXP_INT 7 // 0x000e external int (message from cpu)
typedef struct {
u16 ar[4];
u16 ix[4];
u16 wr[4];
u16 st[4];
u16 cr;
u16 sr;
union {
u64 val;
struct {
u16 l;
u16 m;
u16 h;
u16 m2;//if this gets in the way, drop it.
};
} prod;
union {
u32 val;
struct {
u16 l;
u16 h;
};
} ax[2];
union {
u64 val;
struct {
u16 l;
u16 m;
u16 h;
};
} ac[2];
} DSP_Regs;
// All the state of the DSP should be in this struct. Any DSP state that is not filled on init
// should be moved here.
struct SDSP
{
DSP_Regs r;
u16 pc;
#if PROFILE
u16 err_pc;
#endif
// This is NOT the same cr as r.cr.
// This register is shared with the main emulation, see DSP.cpp
// The plugin has control over 0x0C07 of this reg.
// Bits are defined in a struct in DSP.cpp.
u16 cr;
u8 reg_stack_ptr[4];
u8 exceptions; // pending exceptions
bool external_interrupt_waiting;
// DSP hardware stacks. They're mapped to a bunch of registers, such that writes
// to them push and reads pop.
// Let's make stack depth 32 for now, which is way more than what's needed.
// The real DSP has different depths for the different stacks, but it would
// be strange if any ucode relied on stack overflows since on the DSP, when
// the stack overflows, you're screwed.
u16 reg_stack[4][DSP_STACK_DEPTH];
// For debugging.
u32 iram_crc;
u64 step_counter;
// Mailbox.
volatile u16 mbox[2][2];
// Mutex protecting the mailbox.
Common::CriticalSection g_CriticalSection;
// Accelerator / DMA / other hardware registers. Not GPRs.
u16 ifx_regs[256];
// When state saving, all of the above can just be memcpy'd into the save state.
// The below needs special handling.
u16 *iram;
u16 *dram;
u16 *irom;
u16 *coef;
// This one doesn't really belong here.
u8 *cpu_ram;
};
extern SDSP g_dsp;
extern DSPBreakpoints dsp_breakpoints;
extern DSPEmitter *dspjit;
extern u16 cyclesLeft;
bool DSPCore_Init(const char *irom_filename, const char *coef_filename,
bool bUsingJIT);
void DSPCore_Reset();
void DSPCore_Shutdown(); // Frees all allocated memory.
void DSPCore_CheckExternalInterrupt();
void DSPCore_CheckExceptions();
void DSPCore_SetExternalInterrupt(bool val);
// sets a flag in the pending exception register.
void DSPCore_SetException(u8 level);
void CompileCurrent();
enum DSPCoreState
{
DSPCORE_STOP = 0,
DSPCORE_RUNNING,
DSPCORE_STEPPING,
};
int DSPCore_RunCycles(int cycles);
// These are meant to be called from the UI thread.
void DSPCore_SetState(DSPCoreState new_state);
DSPCoreState DSPCore_GetState();
void DSPCore_Step();
u16 DSPCore_ReadRegister(int reg);
void DSPCore_WriteRegister(int reg, u16 val);
#endif // _DSPCORE_H

View File

@ -0,0 +1,446 @@
// Copyright (C) 2010 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include <cstring>
#include "DSPEmitter.h"
#include "DSPMemoryMap.h"
#include "DSPCore.h"
#include "DSPInterpreter.h"
#include "DSPAnalyzer.h"
#include "Jit/DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#define MAX_BLOCK_SIZE 250
#define DSP_IDLE_SKIP_CYCLES 0x1000
using namespace Gen;
DSPEmitter::DSPEmitter() : gpr(*this), storeIndex(-1), storeIndex2(-1)
{
m_compiledCode = NULL;
AllocCodeSpace(COMPILED_CODE_SIZE);
blocks = new CompiledCode[MAX_BLOCKS];
blockLinks = new Block[MAX_BLOCKS];
blockSize = new u16[MAX_BLOCKS];
unresolvedJumps = new std::list<u16>[MAX_BLOCKS];
compileSR = 0;
compileSR |= SR_INT_ENABLE;
compileSR |= SR_EXT_INT_ENABLE;
CompileDispatcher();
stubEntryPoint = CompileStub();
//clear all of the block references
for(int i = 0x0000; i < MAX_BLOCKS; i++)
{
blocks[i] = (CompiledCode)stubEntryPoint;
blockLinks[i] = 0;
blockSize[i] = 0;
}
}
DSPEmitter::~DSPEmitter()
{
delete[] blocks;
delete[] blockLinks;
delete[] blockSize;
FreeCodeSpace();
}
void DSPEmitter::ClearIRAM() {
// ClearCodeSpace();
for(int i = 0x0000; i < 0x1000; i++)
{
blocks[i] = (CompiledCode)stubEntryPoint;
blockLinks[i] = 0;
blockSize[i] = 0;
}
}
// Must go out of block if exception is detected
void DSPEmitter::checkExceptions(u32 retval)
{
// Check for interrupts and exceptions
#ifdef _M_IX86 // All32
TEST(8, M(&g_dsp.exceptions), Imm8(0xff));
#else
MOV(64, R(RAX), ImmPtr(&g_dsp.exceptions));
TEST(8, MatR(RAX), Imm8(0xff));
#endif
FixupBranch skipCheck = J_CC(CC_Z);
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC));
#endif
DSPJitRegCache c(gpr);
SaveDSPRegs();
ABI_CallFunction((void *)&DSPCore_CheckExceptions);
MOV(32, R(EAX), Imm32(retval));
JMP(returnDispatcher, true);
gpr.flushRegs(c,false);
SetJumpTarget(skipCheck);
}
void DSPEmitter::Default(UDSPInstruction inst)
{
if (opTable[inst]->reads_pc)
{
// Increment PC - we shouldn't need to do this for every instruction. only for branches and end of block.
// Fallbacks to interpreter need this for fetching immediate values
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 1));
#endif
}
// Fall back to interpreter
SaveDSPRegs();
ABI_CallFunctionC16((void*)opTable[inst]->intFunc, inst);
LoadDSPRegs();
}
void DSPEmitter::EmitInstruction(UDSPInstruction inst)
{
const DSPOPCTemplate *tinst = GetOpTemplate(inst);
bool ext_is_jit = false;
// Call extended
if (tinst->extended) {
if ((inst >> 12) == 0x3) {
if (! extOpTable[inst & 0x7F]->jitFunc) {
// Fall back to interpreter
SaveDSPRegs();
ABI_CallFunctionC16((void*)extOpTable[inst & 0x7F]->intFunc, inst);
LoadDSPRegs();
INFO_LOG(DSPLLE, "Instruction not JITed(ext part): %04x\n", inst);
ext_is_jit = false;
} else {
(this->*extOpTable[inst & 0x7F]->jitFunc)(inst);
ext_is_jit = true;
}
} else {
if (!extOpTable[inst & 0xFF]->jitFunc) {
// Fall back to interpreter
SaveDSPRegs();
ABI_CallFunctionC16((void*)extOpTable[inst & 0xFF]->intFunc, inst);
LoadDSPRegs();
INFO_LOG(DSPLLE, "Instruction not JITed(ext part): %04x\n", inst);
ext_is_jit = false;
} else {
(this->*extOpTable[inst & 0xFF]->jitFunc)(inst);
ext_is_jit = true;
}
}
}
// Main instruction
if (!opTable[inst]->jitFunc) {
Default(inst);
INFO_LOG(DSPLLE, "Instruction not JITed(main part): %04x\n", inst);
}
else
{
(this->*opTable[inst]->jitFunc)(inst);
}
// Backlog
if (tinst->extended) {
if (!ext_is_jit) {
//need to call the online cleanup function because
//the writeBackLog gets populated at runtime
SaveDSPRegs();
ABI_CallFunction((void*)::applyWriteBackLog);
LoadDSPRegs();
} else {
popExtValueToReg();
}
}
}
void DSPEmitter::unknown_instruction(UDSPInstruction inst)
{
PanicAlert("unknown_instruction %04x - Fix me ;)", inst);
}
void DSPEmitter::Compile(u16 start_addr)
{
// Remember the current block address for later
startAddr = start_addr;
unresolvedJumps[start_addr].clear();
const u8 *entryPoint = AlignCode16();
/*
// Check for other exceptions
if (dsp_SR_is_flag_set(SR_INT_ENABLE))
return;
if (g_dsp.exceptions == 0)
return;
*/
LoadDSPRegs();
blockLinkEntry = GetCodePtr();
compilePC = start_addr;
bool fixup_pc = false;
blockSize[start_addr] = 0;
while (compilePC < start_addr + MAX_BLOCK_SIZE)
{
if (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_CHECK_INT)
checkExceptions(blockSize[start_addr]);
UDSPInstruction inst = dsp_imem_read(compilePC);
const DSPOPCTemplate *opcode = GetOpTemplate(inst);
EmitInstruction(inst);
blockSize[start_addr]++;
compilePC += opcode->size;
// If the block was trying to link into itself, remove the link
unresolvedJumps[start_addr].remove(compilePC);
fixup_pc = true;
// Handle loop condition, only if current instruction was flagged as a loop destination
// by the analyzer.
if (DSPAnalyzer::code_flags[compilePC-1] & DSPAnalyzer::CODE_LOOP_END)
{
#ifdef _M_IX86 // All32
MOVZX(32, 16, EAX, M(&(g_dsp.r.st[2])));
#else
MOV(64, R(R11), ImmPtr(&g_dsp.r));
MOVZX(32, 16, EAX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[2])));
#endif
CMP(32, R(EAX), Imm32(0));
FixupBranch rLoopAddressExit = J_CC(CC_LE, true);
#ifdef _M_IX86 // All32
MOVZX(32, 16, EAX, M(&g_dsp.r.st[3]));
#else
MOVZX(32, 16, EAX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[3])));
#endif
CMP(32, R(EAX), Imm32(0));
FixupBranch rLoopCounterExit = J_CC(CC_LE, true);
if (!opcode->branch)
{
//branch insns update the g_dsp.pc
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC));
#endif
}
// These functions branch and therefore only need to be called in the
// end of each block and in this order
DSPJitRegCache c(gpr);
HandleLoop();
SaveDSPRegs();
if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
{
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
}
else
{
MOV(16, R(EAX), Imm16(blockSize[start_addr]));
}
JMP(returnDispatcher, true);
gpr.flushRegs(c,false);
SetJumpTarget(rLoopAddressExit);
SetJumpTarget(rLoopCounterExit);
}
if (opcode->branch)
{
//don't update g_dsp.pc -- the branch insn already did
fixup_pc = false;
if (opcode->uncond_branch)
{
break;
}
else if (!opcode->jitFunc)
{
//look at g_dsp.pc if we actually branched
#ifdef _M_IX86 // All32
MOV(16, R(AX), M(&g_dsp.pc));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, R(AX), MatR(RAX));
#endif
CMP(16, R(AX), Imm16(compilePC));
FixupBranch rNoBranch = J_CC(CC_Z);
DSPJitRegCache c(gpr);
//don't update g_dsp.pc -- the branch insn already did
SaveDSPRegs();
if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
{
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
}
else
{
MOV(16, R(EAX), Imm16(blockSize[start_addr]));
}
JMP(returnDispatcher, true);
gpr.flushRegs(c,false);
SetJumpTarget(rNoBranch);
}
}
// End the block if we're before an idle skip address
if (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_IDLE_SKIP)
{
break;
}
}
if (fixup_pc) {
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC));
#endif
}
blocks[start_addr] = (CompiledCode)entryPoint;
// Mark this block as a linkable destination if it does not contain
// any unresolved CALL's
if (unresolvedJumps[start_addr].empty())
{
blockLinks[start_addr] = blockLinkEntry;
for(u16 i = 0x0000; i < 0xffff; ++i)
{
if (!unresolvedJumps[i].empty())
{
// Check if there were any blocks waiting for this block to be linkable
unsigned int size = unresolvedJumps[i].size();
unresolvedJumps[i].remove(start_addr);
if (unresolvedJumps[i].size() < size)
{
// Mark the block to be recompiled again
blocks[i] = (CompiledCode)stubEntryPoint;
blockLinks[i] = 0;
blockSize[i] = 0;
}
}
}
}
if (blockSize[start_addr] == 0)
{
// just a safeguard, should never happen anymore.
// if it does we might get stuck over in RunForCycles.
ERROR_LOG(DSPLLE, "Block at 0x%04x has zero size", start_addr);
blockSize[start_addr] = 1;
}
SaveDSPRegs();
if (DSPAnalyzer::code_flags[start_addr] & DSPAnalyzer::CODE_IDLE_SKIP)
{
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
}
else
{
MOV(16, R(EAX), Imm16(blockSize[start_addr]));
}
JMP(returnDispatcher, true);
}
const u8 *DSPEmitter::CompileStub()
{
const u8 *entryPoint = AlignCode16();
ABI_CallFunction((void *)&CompileCurrent);
//MOVZX(32, 16, ECX, M(&g_dsp.pc));
XOR(32, R(EAX), R(EAX)); // Return 0 cycles executed
JMP(returnDispatcher);
return entryPoint;
}
void DSPEmitter::CompileDispatcher()
{
enterDispatcher = AlignCode16();
ABI_PushAllCalleeSavedRegsAndAdjustStack();
const u8 *dispatcherLoop = GetCodePtr();
// Check for DSP halt
#ifdef _M_IX86
TEST(8, M(&g_dsp.cr), Imm8(CR_HALT));
#else
MOV(64, R(RAX), ImmPtr(&g_dsp.cr));
TEST(8, MatR(RAX), Imm8(CR_HALT));
#endif
FixupBranch _halt = J_CC(CC_NE);
#ifdef _M_IX86
MOVZX(32, 16, ECX, M(&g_dsp.pc));
#else
MOV(64, R(RCX), ImmPtr(&g_dsp.pc));
MOVZX(64, 16, RCX, MatR(RCX));
#endif
// Execute block. Cycles executed returned in EAX.
#ifdef _M_IX86
MOV(32, R(EBX), ImmPtr(blocks));
JMPptr(MComplex(EBX, ECX, SCALE_4, 0));
#else
MOV(64, R(RBX), ImmPtr(blocks));
JMPptr(MComplex(RBX, RCX, SCALE_8, 0));
#endif
returnDispatcher = GetCodePtr();
// Decrement cyclesLeft
#ifdef _M_IX86
SUB(16, M(&cyclesLeft), R(EAX));
#else
MOV(64, R(R12), ImmPtr(&cyclesLeft));
SUB(16, MatR(R12), R(EAX));
#endif
J_CC(CC_A, dispatcherLoop);
// DSP gave up the remaining cycles.
SetJumpTarget(_halt);
//MOV(32, M(&cyclesLeft), Imm32(0));
ABI_PopAllCalleeSavedRegsAndAdjustStack();
RET();
}

View File

@ -0,0 +1,303 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _DSPEMITTER_H
#define _DSPEMITTER_H
#include <list>
#include "DSPCommon.h"
#include "x64Emitter.h"
#include "Jit/DSPJitRegCache.h"
#define COMPILED_CODE_SIZE sizeof(void *) * 0x200000
#define MAX_BLOCKS 0x10000
typedef u32 (*CompiledCode)();
typedef const u8 *Block;
class DSPEmitter : public Gen::XCodeBlock, NonCopyable
{
public:
DSPEmitter();
~DSPEmitter();
Block m_compiledCode;
void EmitInstruction(UDSPInstruction inst);
void unknown_instruction(UDSPInstruction inst);
void ClearIRAM();
void CompileDispatcher();
Block CompileStub();
void Compile(u16 start_addr);
void ClearCallFlag();
void Default(UDSPInstruction inst);
// CC Util
void Update_SR_Register64(Gen::X64Reg val = Gen::EAX);
void Update_SR_Register64_Carry(Gen::X64Reg val = Gen::EAX);
void Update_SR_Register64_Carry2(Gen::X64Reg val = Gen::EAX);
void Update_SR_Register16(Gen::X64Reg val = Gen::EAX);
void Update_SR_Register16_OverS32(Gen::X64Reg val = Gen::EAX);
// Register helpers
void setCompileSR(u16 bit);
void clrCompileSR(u16 bit);
void checkExceptions(u32 retval);
// Memory helper functions
void increment_addr_reg(int reg);
void decrement_addr_reg(int reg);
void increase_addr_reg(int reg);
void decrease_addr_reg(int reg);
void imem_read();
void dmem_read();
void dmem_read_imm(u16 addr);
void dmem_write();
void dmem_write_imm(u16 addr);
// Ext command helpers
void pushExtValueFromReg(u16 dreg, u16 sreg);
void popExtValueToReg();
void pushExtValueFromMem(u16 dreg, u16 sreg);
void pushExtValueFromMem2(u16 dreg, u16 sreg);
void zeroWriteBackLog(const UDSPInstruction opc);
// Ext commands
void l(const UDSPInstruction opc);
void ln(const UDSPInstruction opc);
void ls(const UDSPInstruction opc);
void lsn(const UDSPInstruction opc);
void lsm(const UDSPInstruction opc);
void lsnm(const UDSPInstruction opc);
void sl(const UDSPInstruction opc);
void sln(const UDSPInstruction opc);
void slm(const UDSPInstruction opc);
void slnm(const UDSPInstruction opc);
void s(const UDSPInstruction opc);
void sn(const UDSPInstruction opc);
void ld(const UDSPInstruction opc);
void ldn(const UDSPInstruction opc);
void ldm(const UDSPInstruction opc);
void ldnm(const UDSPInstruction opc);
void mv(const UDSPInstruction opc);
void dr(const UDSPInstruction opc);
void ir(const UDSPInstruction opc);
void nr(const UDSPInstruction opc);
void nop(const UDSPInstruction opc) {}
// Command helpers
void dsp_reg_stack_push(int stack_reg);
void dsp_reg_stack_pop(int stack_reg);
void dsp_reg_store_stack(int stack_reg, Gen::X64Reg host_sreg = Gen::EDX);
void dsp_reg_load_stack(int stack_reg, Gen::X64Reg host_dreg = Gen::EDX);
void dsp_reg_store_stack_imm(int stack_reg, u16 val);
void dsp_op_write_reg(int reg, Gen::X64Reg host_sreg);
void dsp_op_write_reg_imm(int reg, u16 val);
void dsp_conditional_extend_accum(int reg);
void dsp_conditional_extend_accum_imm(int reg, u16 val);
void dsp_op_read_reg(int reg, Gen::X64Reg host_dreg, DSPJitSignExtend extend = NONE);
// Commands
void dar(const UDSPInstruction opc);
void iar(const UDSPInstruction opc);
void subarn(const UDSPInstruction opc);
void addarn(const UDSPInstruction opc);
void sbclr(const UDSPInstruction opc);
void sbset(const UDSPInstruction opc);
void srbith(const UDSPInstruction opc);
void lri(const UDSPInstruction opc);
void lris(const UDSPInstruction opc);
void mrr(const UDSPInstruction opc);
void nx(const UDSPInstruction opc);
// Branch
void HandleLoop();
void jcc(const UDSPInstruction opc);
void jmprcc(const UDSPInstruction opc);
void call(const UDSPInstruction opc);
void callr(const UDSPInstruction opc);
void ifcc(const UDSPInstruction opc);
void ret(const UDSPInstruction opc);
void rti(const UDSPInstruction opc);
void halt(const UDSPInstruction opc);
void loop(const UDSPInstruction opc);
void loopi(const UDSPInstruction opc);
void bloop(const UDSPInstruction opc);
void bloopi(const UDSPInstruction opc);
// Load/Store
void srs(const UDSPInstruction opc);
void lrs(const UDSPInstruction opc);
void lr(const UDSPInstruction opc);
void sr(const UDSPInstruction opc);
void si(const UDSPInstruction opc);
void lrr(const UDSPInstruction opc);
void lrrd(const UDSPInstruction opc);
void lrri(const UDSPInstruction opc);
void lrrn(const UDSPInstruction opc);
void srr(const UDSPInstruction opc);
void srrd(const UDSPInstruction opc);
void srri(const UDSPInstruction opc);
void srrn(const UDSPInstruction opc);
void ilrr(const UDSPInstruction opc);
void ilrrd(const UDSPInstruction opc);
void ilrri(const UDSPInstruction opc);
void ilrrn(const UDSPInstruction opc);
// Arithmetic
void clr(const UDSPInstruction opc);
void clrl(const UDSPInstruction opc);
void andcf(const UDSPInstruction opc);
void andf(const UDSPInstruction opc);
void tst(const UDSPInstruction opc);
void tstaxh(const UDSPInstruction opc);
void cmp(const UDSPInstruction opc);
void cmpar(const UDSPInstruction opc);
void cmpi(const UDSPInstruction opc);
void cmpis(const UDSPInstruction opc);
void xorr(const UDSPInstruction opc);
void andr(const UDSPInstruction opc);
void orr(const UDSPInstruction opc);
void andc(const UDSPInstruction opc);
void orc(const UDSPInstruction opc);
void xorc(const UDSPInstruction opc);
void notc(const UDSPInstruction opc);
void xori(const UDSPInstruction opc);
void andi(const UDSPInstruction opc);
void ori(const UDSPInstruction opc);
void addr(const UDSPInstruction opc);
void addax(const UDSPInstruction opc);
void add(const UDSPInstruction opc);
void addp(const UDSPInstruction opc);
void addaxl(const UDSPInstruction opc);
void addi(const UDSPInstruction opc);
void addis(const UDSPInstruction opc);
void incm(const UDSPInstruction opc);
void inc(const UDSPInstruction opc);
void subr(const UDSPInstruction opc);
void subax(const UDSPInstruction opc);
void sub(const UDSPInstruction opc);
void subp(const UDSPInstruction opc);
void decm(const UDSPInstruction opc);
void dec(const UDSPInstruction opc);
void neg(const UDSPInstruction opc);
void abs(const UDSPInstruction opc);
void movr(const UDSPInstruction opc);
void movax(const UDSPInstruction opc);
void mov(const UDSPInstruction opc);
void lsl16(const UDSPInstruction opc);
void lsr16(const UDSPInstruction opc);
void asr16(const UDSPInstruction opc);
void lsl(const UDSPInstruction opc);
void lsr(const UDSPInstruction opc);
void asl(const UDSPInstruction opc);
void asr(const UDSPInstruction opc);
void lsrn(const UDSPInstruction opc);
void asrn(const UDSPInstruction opc);
void lsrnrx(const UDSPInstruction opc);
void asrnrx(const UDSPInstruction opc);
void lsrnr(const UDSPInstruction opc);
void asrnr(const UDSPInstruction opc);
// Multipliers
void multiply();
void multiply_add();
void multiply_sub();
void multiply_mulx(u8 axh0, u8 axh1);
void clrp(const UDSPInstruction opc);
void tstprod(const UDSPInstruction opc);
void movp(const UDSPInstruction opc);
void movnp(const UDSPInstruction opc);
void movpz(const UDSPInstruction opc);
void addpaxz(const UDSPInstruction opc);
void mulaxh(const UDSPInstruction opc);
void mul(const UDSPInstruction opc);
void mulac(const UDSPInstruction opc);
void mulmv(const UDSPInstruction opc);
void mulmvz(const UDSPInstruction opc);
void mulx(const UDSPInstruction opc);
void mulxac(const UDSPInstruction opc);
void mulxmv(const UDSPInstruction opc);
void mulxmvz(const UDSPInstruction opc);
void mulc(const UDSPInstruction opc);
void mulcac(const UDSPInstruction opc);
void mulcmv(const UDSPInstruction opc);
void mulcmvz(const UDSPInstruction opc);
void maddx(const UDSPInstruction opc);
void msubx(const UDSPInstruction opc);
void maddc(const UDSPInstruction opc);
void msubc(const UDSPInstruction opc);
void madd(const UDSPInstruction opc);
void msub(const UDSPInstruction opc);
// CALL this to start the dispatcher
const u8 *enterDispatcher;
const u8 *stubEntryPoint;
const u8 *returnDispatcher;
u16 compilePC;
u16 startAddr;
Block *blockLinks;
u16 *blockSize;
std::list<u16> *unresolvedJumps;
DSPJitRegCache gpr;
void LoadDSPRegs();
void SaveDSPRegs();
private:
CompiledCode *blocks;
Block blockLinkEntry;
u16 compileSR;
// The index of the last stored ext value (compile time).
int storeIndex;
int storeIndex2;
// Counts down.
// int cycles;
void Update_SR_Register(Gen::X64Reg val = Gen::EAX);
void ToMask(Gen::X64Reg value_reg = Gen::EDI);
void dsp_increment_one(Gen::X64Reg ar = Gen::EAX, Gen::X64Reg wr = Gen::EDX, Gen::X64Reg wr_pow = Gen::EDI, Gen::X64Reg temp_reg = Gen::ESI);
void dsp_decrement_one(Gen::X64Reg ar = Gen::EAX, Gen::X64Reg wr = Gen::EDX, Gen::X64Reg wr_pow = Gen::EDI, Gen::X64Reg temp_reg = Gen::ESI);
void get_long_prod(Gen::X64Reg long_prod = Gen::RAX);
void get_long_prod_round_prodl(Gen::X64Reg long_prod = Gen::RAX);
void set_long_prod();
void round_long_acc(Gen::X64Reg long_acc = Gen::EAX);
void set_long_acc(int _reg, Gen::X64Reg acc = Gen::EAX);
void get_acc_h(int _reg, Gen::X64Reg acc = Gen::EAX, bool sign = true);
void set_acc_h(int _reg, Gen::OpArg arg = R(Gen::EAX));
void get_acc_m(int _reg, Gen::X64Reg acc = Gen::EAX, bool sign = true);
void set_acc_m(int _reg, Gen::OpArg arg = R(Gen::EAX));
void get_acc_l(int _reg, Gen::X64Reg acc = Gen::EAX, bool sign = true);
void set_acc_l(int _reg, Gen::OpArg arg = R(Gen::EAX));
void get_long_acx(int _reg, Gen::X64Reg acx = Gen::EAX);
void get_ax_l(int _reg, Gen::X64Reg acx = Gen::EAX);
void get_ax_h(int _reg, Gen::X64Reg acc = Gen::EAX);
void get_long_acc(int _reg, Gen::X64Reg acc = Gen::EAX);
};
#endif // _DSPEMITTER_H

View File

@ -0,0 +1,361 @@
/*====================================================================
filename: gdsp_interface.h
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 "Thread.h"
#include "MemoryUtil.h"
#include "DSPCore.h"
#include "DSPHost.h"
#include "DSPTables.h"
#include "DSPAnalyzer.h"
#include "DSPAccelerator.h"
#include "DSPInterpreter.h"
#include "DSPHWInterface.h"
#include "CPUDetect.h"
#if _M_SSE >= 0x301 && !(defined __GNUC__ && !defined __SSSE3__)
#include <tmmintrin.h>
#endif
void gdsp_do_dma();
Common::CriticalSection g_CriticalSection;
void gdsp_ifx_init()
{
for (int i = 0; i < 256; i++)
{
g_dsp.ifx_regs[i] = 0;
}
g_dsp.mbox[0][0] = 0;
g_dsp.mbox[0][1] = 0;
g_dsp.mbox[1][0] = 0;
g_dsp.mbox[1][1] = 0;
}
u32 gdsp_mbox_peek(u8 mbx)
{
if (DSPHost_OnThread())
g_CriticalSection.Enter();
u32 value = ((g_dsp.mbox[mbx][0] << 16) | g_dsp.mbox[mbx][1]);
if (DSPHost_OnThread())
g_CriticalSection.Leave();
return value;
}
void gdsp_mbox_write_h(u8 mbx, u16 val)
{
if (DSPHost_OnThread())
g_CriticalSection.Enter();
g_dsp.mbox[mbx][0] = val & 0x7fff;
if (DSPHost_OnThread())
g_CriticalSection.Leave();
}
void gdsp_mbox_write_l(u8 mbx, u16 val)
{
if (DSPHost_OnThread())
g_CriticalSection.Enter();
g_dsp.mbox[mbx][1] = val;
g_dsp.mbox[mbx][0] |= 0x8000;
if (DSPHost_OnThread())
g_CriticalSection.Leave();
#if defined(_DEBUG) || defined(DEBUGFAST)
if (mbx == GDSP_MBOX_DSP)
{
NOTICE_LOG(DSP_MAIL, "DSP(WM) B:%i M:0x%08x (pc=0x%04x)", mbx, gdsp_mbox_peek(GDSP_MBOX_DSP), g_dsp.pc);
} else {
NOTICE_LOG(DSP_MAIL, "CPU(WM) B:%i M:0x%08x (pc=0x%04x)", mbx, gdsp_mbox_peek(GDSP_MBOX_CPU), g_dsp.pc);
}
#endif
}
u16 gdsp_mbox_read_h(u8 mbx)
{
return g_dsp.mbox[mbx][0]; // TODO: mask away the top bit?
}
u16 gdsp_mbox_read_l(u8 mbx)
{
if (DSPHost_OnThread())
g_CriticalSection.Enter();
u16 val = g_dsp.mbox[mbx][1];
g_dsp.mbox[mbx][0] &= ~0x8000;
if (DSPHost_OnThread())
g_CriticalSection.Leave();
#if defined(_DEBUG) || defined(DEBUGFAST)
if (mbx == GDSP_MBOX_DSP)
{
NOTICE_LOG(DSP_MAIL, "DSP(RM) B:%i M:0x%08x (pc=0x%04x)", mbx, gdsp_mbox_peek(GDSP_MBOX_DSP), g_dsp.pc);
} else {
NOTICE_LOG(DSP_MAIL, "CPU(RM) B:%i M:0x%08x (pc=0x%04x)", mbx, gdsp_mbox_peek(GDSP_MBOX_CPU), g_dsp.pc);
}
#endif
return val;
}
void gdsp_ifx_write(u32 addr, u32 val)
{
switch (addr & 0xff)
{
case DSP_DIRQ:
if (val & 0x1)
DSPHost_InterruptRequest();
else
INFO_LOG(DSPLLE, "Unknown Interrupt Request pc=%04x (%04x)", g_dsp.pc, val);
break;
case DSP_DMBH:
gdsp_mbox_write_h(GDSP_MBOX_DSP, val);
break;
case DSP_DMBL:
gdsp_mbox_write_l(GDSP_MBOX_DSP, val);
break;
case DSP_CMBH:
return gdsp_mbox_write_h(GDSP_MBOX_CPU, val);
case DSP_CMBL:
return gdsp_mbox_write_l(GDSP_MBOX_CPU, val);
case DSP_DSBL:
g_dsp.ifx_regs[DSP_DSBL] = val;
g_dsp.ifx_regs[DSP_DSCR] |= 4; // Doesn't really matter since we do DMA instantly
if (!g_dsp.ifx_regs[DSP_AMDM])
gdsp_do_dma();
else
NOTICE_LOG(DSPLLE, "Masked DMA skipped");
g_dsp.ifx_regs[DSP_DSCR] &= ~4;
g_dsp.ifx_regs[DSP_DSBL] = 0;
break;
case DSP_ACDATA1: // Accelerator write (Zelda type) - "UnkZelda"
dsp_write_aram_d3(val);
break;
case DSP_GAIN:
if (val) {
INFO_LOG(DSPLLE,"Gain Written: 0x%04x", val);
}
case DSP_DSPA:
case DSP_DSMAH:
case DSP_DSMAL:
case DSP_DSCR:
g_dsp.ifx_regs[addr & 0xFF] = val;
break;
/*
case DSP_ACCAL:
dsp_step_accelerator();
break;
*/
default:
if ((addr & 0xff) >= 0xa0) {
if (pdlabels[(addr & 0xFF) - 0xa0].name && pdlabels[(addr & 0xFF) - 0xa0].description) {
INFO_LOG(DSPLLE, "%04x MW %s (%04x)", g_dsp.pc, pdlabels[(addr & 0xFF) - 0xa0].name, val);
}
else {
ERROR_LOG(DSPLLE, "%04x MW %04x (%04x)", g_dsp.pc, addr, val);
}
}
else {
ERROR_LOG(DSPLLE, "%04x MW %04x (%04x)", g_dsp.pc, addr, val);
}
g_dsp.ifx_regs[addr & 0xFF] = val;
break;
}
}
u16 gdsp_ifx_read(u16 addr)
{
switch (addr & 0xff)
{
case DSP_DMBH:
return gdsp_mbox_read_h(GDSP_MBOX_DSP);
case DSP_DMBL:
return gdsp_mbox_read_l(GDSP_MBOX_DSP);
case DSP_CMBH:
return gdsp_mbox_read_h(GDSP_MBOX_CPU);
case DSP_CMBL:
return gdsp_mbox_read_l(GDSP_MBOX_CPU);
case DSP_DSCR:
return g_dsp.ifx_regs[addr & 0xFF];
case DSP_ACCELERATOR: // ADPCM Accelerator reads
return dsp_read_accelerator();
case DSP_ACDATA1: // Accelerator reads (Zelda type) - "UnkZelda"
return dsp_read_aram_d3();
default:
if ((addr & 0xff) >= 0xa0) {
if (pdlabels[(addr & 0xFF) - 0xa0].name && pdlabels[(addr & 0xFF) - 0xa0].description) {
INFO_LOG(DSPLLE, "%04x MR %s (%04x)", g_dsp.pc, pdlabels[(addr & 0xFF) - 0xa0].name, g_dsp.ifx_regs[addr & 0xFF]);
}
else {
ERROR_LOG(DSPLLE, "%04x MR %04x (%04x)", g_dsp.pc, addr, g_dsp.ifx_regs[addr & 0xFF]);
}
}
else {
ERROR_LOG(DSPLLE, "%04x MR %04x (%04x)", g_dsp.pc, addr, g_dsp.ifx_regs[addr & 0xFF]);
}
return g_dsp.ifx_regs[addr & 0xFF];
}
}
void gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size)
{
UnWriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
u8* dst = ((u8*)g_dsp.iram);
for (u32 i = 0; i < size; i += 2)
{
// TODO : this may be different on Wii.
*(u16*)&dst[dsp_addr + i] = Common::swap16(*(const u16*)&g_dsp.cpu_ram[(addr + i) & 0x0fffffff]);
}
WriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
g_dsp.iram_crc = DSPHost_CodeLoaded(g_dsp.cpu_ram + (addr & 0x0fffffff), size);
NOTICE_LOG(DSPLLE, "*** Copy new UCode from 0x%08x to 0x%04x (crc: %8x)", addr, dsp_addr, g_dsp.iram_crc);
if (dspjit)
dspjit->ClearIRAM();
DSPAnalyzer::Analyze();
}
void gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size)
{
ERROR_LOG(DSPLLE, "*** idma_out IRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
}
static const __m128i s_mask = _mm_set_epi32(0x0E0F0C0DL, 0x0A0B0809L, 0x06070405L, 0x02030001L);
// TODO: These should eat clock cycles.
void gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size)
{
u8* dst = ((u8*)g_dsp.dram);
#if _M_SSE >= 0x301
if (cpu_info.bSSSE3 && !(size % 16))
{
for (u32 i = 0; i < size; i += 16)
{
_mm_storeu_si128((__m128i *)&dst[dsp_addr + i], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&g_dsp.cpu_ram[(addr + i) & 0x7FFFFFFF]), s_mask));
}
}
else
#endif
{
for (u32 i = 0; i < size; i += 2)
{
*(u16*)&dst[dsp_addr + i] = Common::swap16(*(const u16*)&g_dsp.cpu_ram[(addr + i) & 0x7FFFFFFF]);
}
}
INFO_LOG(DSPLLE, "*** ddma_in RAM (0x%08x) -> DRAM_DSP (0x%04x) : size (0x%08x)", addr, dsp_addr / 2, size);
}
void gdsp_ddma_out(u16 dsp_addr, u32 addr, u32 size)
{
const u8* src = ((const u8*)g_dsp.dram);
#if _M_SSE >= 0x301
if (cpu_info.bSSSE3 && !(size % 16))
{
for (u32 i = 0; i < size; i += 16)
{
_mm_storeu_si128((__m128i *)&g_dsp.cpu_ram[(addr + i) & 0x7FFFFFFF], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&src[dsp_addr + i]), s_mask));
}
}
else
#endif
{
for (u32 i = 0; i < size; i += 2)
{
*(u16*)&g_dsp.cpu_ram[(addr + i) & 0x7FFFFFFF] = Common::swap16(*(const u16*)&src[dsp_addr + i]);
}
}
INFO_LOG(DSPLLE, "*** ddma_out DRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
}
void gdsp_do_dma()
{
u16 ctl;
u32 addr;
u16 dsp_addr;
u16 len;
addr = (g_dsp.ifx_regs[DSP_DSMAH] << 16) | g_dsp.ifx_regs[DSP_DSMAL];
ctl = g_dsp.ifx_regs[DSP_DSCR];
dsp_addr = g_dsp.ifx_regs[DSP_DSPA] * 2;
len = g_dsp.ifx_regs[DSP_DSBL];
if (len > 0x4000)
{
ERROR_LOG(DSPLLE, "DMA ERROR pc: %04x ctl: %04x addr: %08x da: %04x size: %04x", g_dsp.pc, ctl, addr, dsp_addr, len);
exit(0);
}
#if defined(_DEBUG) || defined(DEBUGFAST)
DEBUG_LOG(DSPLLE, "DMA pc: %04x ctl: %04x addr: %08x da: %04x size: %04x", g_dsp.pc, ctl, addr, dsp_addr, len);
#endif
switch (ctl & 0x3)
{
case (DSP_CR_DMEM | DSP_CR_TO_CPU):
gdsp_ddma_out(dsp_addr, addr, len);
break;
case (DSP_CR_DMEM | DSP_CR_FROM_CPU):
gdsp_ddma_in(dsp_addr, addr, len);
break;
case (DSP_CR_IMEM | DSP_CR_TO_CPU):
gdsp_idma_out(dsp_addr, addr, len);
break;
case (DSP_CR_IMEM | DSP_CR_FROM_CPU):
gdsp_idma_in(dsp_addr, addr, len);
break;
}
}

View File

@ -0,0 +1,47 @@
/*====================================================================
filename: gdsp_interface.h
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.
====================================================================*/
#ifndef _GDSP_INTERFACE_H
#define _GDSP_INTERFACE_H
#include "Common.h"
#define GDSP_MBOX_CPU 0
#define GDSP_MBOX_DSP 1
u32 gdsp_mbox_peek(u8 mbx);
void gdsp_mbox_write_h(u8 mbx, u16 val);
void gdsp_mbox_write_l(u8 mbx, u16 val);
u16 gdsp_mbox_read_h(u8 mbx);
u16 gdsp_mbox_read_l(u8 mbx);
void gdsp_ifx_init();
void gdsp_ifx_write(u32 addr, u32 val);
u16 gdsp_ifx_read(u16 addr);
void gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size);
#endif

View File

@ -0,0 +1,34 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _DSPHOST_H
#define _DSPHOST_H
// The user of the DSPCore library must supply a few functions so that the
// emulation core can access the environment it runs in. If the emulation
// core isn't used, for example in an asm/disasm tool, then most of these
// can be stubbed out.
u8 DSPHost_ReadHostMemory(u32 addr);
void DSPHost_WriteHostMemory(u8 value, u32 addr);
bool DSPHost_OnThread();
bool DSPHost_Running();
void DSPHost_InterruptRequest();
u32 DSPHost_CodeLoaded(const u8 *ptr, int size);
void DSPHost_UpdateDebugger();
#endif

View File

@ -0,0 +1,197 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie and Tratax (c) 2004
// HELPER FUNCTIONS
#include "DSPIntCCUtil.h"
#include "DSPCore.h"
#include "DSPInterpreter.h"
namespace DSPInterpreter {
void Update_SR_Register64(s64 _Value, bool carry, bool overflow)
{
g_dsp.r.sr &= ~SR_CMP_MASK;
// 0x01
if (carry)
{
g_dsp.r.sr |= SR_CARRY;
}
// 0x02 and 0x80
if (overflow)
{
g_dsp.r.sr |= SR_OVERFLOW;
g_dsp.r.sr |= SR_OVERFLOW_STICKY;
}
// 0x04
if (_Value == 0)
{
g_dsp.r.sr |= SR_ARITH_ZERO;
}
// 0x08
if (_Value < 0)
{
g_dsp.r.sr |= SR_SIGN;
}
// 0x10
if (_Value != (s32)_Value)
{
g_dsp.r.sr |= SR_OVER_S32;
}
// 0x20 - Checks if top bits of m are equal
if (((_Value & 0xc0000000) == 0) || ((_Value & 0xc0000000) == 0xc0000000))
{
g_dsp.r.sr |= SR_TOP2BITS;
}
}
void Update_SR_Register16(s16 _Value, bool carry, bool overflow, bool overS32)
{
g_dsp.r.sr &= ~SR_CMP_MASK;
// 0x01
if (carry)
{
g_dsp.r.sr |= SR_CARRY;
}
// 0x02 and 0x80
if (overflow)
{
g_dsp.r.sr |= SR_OVERFLOW;
g_dsp.r.sr |= SR_OVERFLOW_STICKY;
}
// 0x04
if (_Value == 0)
{
g_dsp.r.sr |= SR_ARITH_ZERO;
}
// 0x08
if (_Value < 0)
{
g_dsp.r.sr |= SR_SIGN;
}
// 0x10
if (overS32)
{
g_dsp.r.sr |= SR_OVER_S32;
}
// 0x20 - Checks if top bits of m are equal
if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
{
g_dsp.r.sr |= SR_TOP2BITS;
}
}
void Update_SR_LZ(bool value)
{
if (value == true)
g_dsp.r.sr |= SR_LOGIC_ZERO;
else
g_dsp.r.sr &= ~SR_LOGIC_ZERO;
}
inline int GetMultiplyModifier()
{
return (g_dsp.r.sr & SR_MUL_MODIFY)?1:2;
}
inline bool isCarry() {
return (g_dsp.r.sr & SR_CARRY) ? true : false;
}
inline bool isOverflow() {
return (g_dsp.r.sr & SR_OVERFLOW) ? true : false;
}
inline bool isOverS32() {
return (g_dsp.r.sr & SR_OVER_S32) ? true : false;
}
inline bool isLess() {
return (!(g_dsp.r.sr & SR_OVERFLOW) != !(g_dsp.r.sr & SR_SIGN));
}
inline bool isZero() {
return (g_dsp.r.sr & SR_ARITH_ZERO) ? true : false;
}
inline bool isLogicZero() {
return (g_dsp.r.sr & SR_LOGIC_ZERO) ? true : false;
}
inline bool isConditionA() {
return (((g_dsp.r.sr & SR_OVER_S32) || (g_dsp.r.sr & SR_TOP2BITS)) && !(g_dsp.r.sr & SR_ARITH_ZERO)) ? true : false;
}
//see DSPCore.h for flags
bool CheckCondition(u8 _Condition)
{
switch (_Condition & 0xf)
{
case 0xf: // Always true.
return true;
case 0x0: // GE - Greater Equal
return !isLess();
case 0x1: // L - Less
return isLess();
case 0x2: // G - Greater
return !isLess() && !isZero();
case 0x3: // LE - Less Equal
return isLess() || isZero();
case 0x4: // NZ - Not Zero
return !isZero();
case 0x5: // Z - Zero
return isZero();
case 0x6: // NC - Not carry
return !isCarry();
case 0x7: // C - Carry
return isCarry();
case 0x8: // ? - Not over s32
return !isOverS32();
case 0x9: // ? - Over s32
return isOverS32();
case 0xa: // ?
return isConditionA();
case 0xb: // ?
return !isConditionA();
case 0xc: // LNZ - Logic Not Zero
return !isLogicZero();
case 0xd: // LZ - Logic Zero
return isLogicZero();
case 0xe: // 0 - Overflow
return isOverflow();
default:
return true;
}
}
} // namespace

View File

@ -0,0 +1,55 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie and Tratax (c) 2004
#ifndef _GDSP_CONDITION_CODES_H
#define _GDSP_CONDITION_CODES_H
// Anything to do with SR and conditions goes here.
#include "Common.h"
namespace DSPInterpreter {
bool CheckCondition(u8 _Condition);
int GetMultiplyModifier();
void Update_SR_Register16(s16 _Value, bool carry = false, bool overflow = false, bool overS32 = false);
void Update_SR_Register64(s64 _Value, bool carry = false, bool overflow = false);
void Update_SR_LZ(bool value);
inline bool isCarry(u64 val, u64 result) {
return (val>result);
}
inline bool isCarry2(u64 val, u64 result) {
return (val>=result);
}
inline bool isOverflow(s64 val1, s64 val2, s64 res) {
return ((val1 ^ res) & (val2 ^ res)) < 0;
}
inline bool isOverS32(s64 acc) {
return (acc != (s32)acc) ? true : false;
}
} // namespace
#endif // _GDSP_CONDITION_CODES_H

View File

@ -0,0 +1,526 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "DSPIntUtil.h"
#include "DSPMemoryMap.h"
#include "DSPIntExtOps.h"
// Extended opcodes do not exist on their own. These opcodes can only be
// attached to opcodes that allow extending (8 (or 7) lower bits of opcode not used by
// opcode). Extended opcodes do not modify program counter $pc register.
// Most of the suffixes increment or decrement one or more addressing registers
// (the first four, ARx). The increment/decrement is either 1, or the
// corresponding "index" register (the second four, IXx). The addressing
// registers will wrap in odd ways, dictated by the corresponding wrapping
// register, WR0-3.
// Needs comments.
inline static void writeToBackLog(int i, int idx, u16 value)
{
writeBackLog[i] = value;
writeBackLogIdx[i] = idx;
}
namespace DSPInterpreter
{
namespace Ext
{
inline bool IsSameMemArea(u16 a, u16 b)
{
//LM: tested on WII
if ((a>>10)==(b>>10))
return true;
else
return false;
}
// DR $arR
// xxxx xxxx 0000 01rr
// Decrement addressing register $arR.
void dr(const UDSPInstruction opc) {
writeToBackLog(0, opc & 0x3, dsp_decrement_addr_reg(opc & 0x3));
}
// IR $arR
// xxxx xxxx 0000 10rr
// Increment addressing register $arR.
void ir(const UDSPInstruction opc) {
writeToBackLog(0, opc & 0x3, dsp_increment_addr_reg(opc & 0x3));
}
// NR $arR
// xxxx xxxx 0000 11rr
// Add corresponding indexing register $ixR to addressing register $arR.
void nr(const UDSPInstruction opc) {
u8 reg = opc & 0x3;
writeToBackLog(0, reg, dsp_increase_addr_reg(reg, (s16)g_dsp.r.ix[reg]));
}
// MV $axD.D, $acS.S
// xxxx xxxx 0001 ddss
// Move value of $acS.S to the $axD.D.
void mv(const UDSPInstruction opc)
{
u8 sreg = (opc & 0x3) + DSP_REG_ACL0;
u8 dreg = ((opc >> 2) & 0x3);
switch(sreg) {
case DSP_REG_ACL0:
case DSP_REG_ACL1:
writeToBackLog(0, dreg + DSP_REG_AXL0, g_dsp.r.ac[sreg-DSP_REG_ACL0].l);
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
writeToBackLog(0, dreg + DSP_REG_AXL0, g_dsp.r.ac[sreg-DSP_REG_ACM0].m);
break;
}
}
// S @$arD, $acS.S
// xxxx xxxx 001s s0dd
// Store value of $acS.S in the memory pointed by register $arD.
// Post increment register $arD.
void s(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
switch(sreg) {
case DSP_REG_ACL0:
case DSP_REG_ACL1:
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg-DSP_REG_ACL0].l);
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg-DSP_REG_ACM0].m);
break;
}
writeToBackLog(0, dreg, dsp_increment_addr_reg(dreg));
}
// SN @$arD, $acS.S
// xxxx xxxx 001s s1dd
// Store value of register $acS.S in the memory pointed by register $arD.
// Add indexing register $ixD to register $arD.
void sn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
switch(sreg) {
case DSP_REG_ACL0:
case DSP_REG_ACL1:
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg-DSP_REG_ACL0].l);
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg-DSP_REG_ACM0].m);
break;
}
writeToBackLog(0, dreg, dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]));
}
// L $axD.D, @$arS
// xxxx xxxx 01dd d0ss
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
// Post increment register $arS.
void l(const UDSPInstruction opc)
{
u8 sreg = opc & 0x3;
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
{
u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
writeToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
writeToBackLog(1, dreg, val);
writeToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
writeToBackLog(3, sreg, dsp_increment_addr_reg(sreg));
}
else
{
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
writeToBackLog(1, sreg, dsp_increment_addr_reg(sreg));
}
}
// LN $axD.D, @$arS
// xxxx xxxx 01dd d0ss
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
// Add indexing register register $ixS to register $arS.
void ln(const UDSPInstruction opc)
{
u8 sreg = opc & 0x3;
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
{
u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
writeToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
writeToBackLog(1, dreg, val);
writeToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
writeToBackLog(3, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
}
else
{
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
writeToBackLog(1, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
}
}
// LS $axD.D, $acS.m108
// xxxx xxxx 10dd 000s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Increment both $ar0 and $ar3.
void ls(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[3], g_dsp.r.ac[sreg].m);
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
}
// LSN $axD.D, $acS.m
// xxxx xxxx 10dd 010s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix0 to addressing
// register $ar0 and increment $ar3.
void lsn(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[3], g_dsp.r.ac[sreg].m);
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
}
// LSM $axD.D, $acS.m
// xxxx xxxx 10dd 100s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix3 to addressing
// register $ar3 and increment $ar0.
void lsm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[3], g_dsp.r.ac[sreg].m);
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
}
// LSMN $axD.D, $acS.m
// xxxx xxxx 10dd 110s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix0 to addressing
// register $ar0 and add corresponding indexing register $ix3 to addressing
// register $ar3.
void lsnm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[3], g_dsp.r.ac[sreg].m);
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
}
// SL $acS.m, $axD.D
// xxxx xxxx 10dd 001s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Increment both $ar0 and $ar3.
void sl(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[0], g_dsp.r.ac[sreg].m);
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
}
// SLN $acS.m, $axD.D
// xxxx xxxx 10dd 011s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
// and increment $ar3.
void sln(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[0], g_dsp.r.ac[sreg].m);
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
}
// SLM $acS.m, $axD.D
// xxxx xxxx 10dd 101s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix3 to addressing register $ar3
// and increment $ar0.
void slm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[0], g_dsp.r.ac[sreg].m);
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
writeToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
}
// SLMN $acS.m, $axD.D
// xxxx xxxx 10dd 111s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
// and add corresponding indexing register $ix3 to addressing register $ar3.
void slnm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_dmem_write(g_dsp.r.ar[0], g_dsp.r.ac[sreg].m);
writeToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
writeToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
}
// LD $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 00ss
// example for "nx'ld $AX0.L, $AX1.L, @$AR3"
// Loads the word pointed by AR0 to AX0.H, then loads the word pointed by AR3 to AX0.L.
// Increments AR0 and AR3.
// If AR0 and AR3 point into the same memory page (upper 6 bits of addr are the same -> games are not doing that!)
// then the value pointed by AR0 is loaded to BOTH AX0.H and AX0.L.
// If AR0 points into an invalid memory page (ie 0x2000), then AX0.H keeps its old value. (not implemented yet)
// If AR3 points into an invalid memory page, then AX0.L gets the same value as AX0.H. (not implemented yet)
void ld(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
if (sreg != DSP_REG_AR3) {
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
} else {
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[dreg]));
if (IsSameMemArea(g_dsp.r.ar[dreg], g_dsp.r.ar[3]))
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[dreg]));
else
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, dreg, dsp_increment_addr_reg(dreg));
}
writeToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
}
// LDN $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 01ss
void ldn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
if (sreg != DSP_REG_AR3) {
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
} else {
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[dreg]));
if (IsSameMemArea(g_dsp.r.ar[dreg], g_dsp.r.ar[3]))
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[dreg]));
else
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, dreg, dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]));
}
writeToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
}
// LDM $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 10ss
void ldm(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
if (sreg != DSP_REG_AR3) {
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
} else {
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[dreg]));
if (IsSameMemArea(g_dsp.r.ar[dreg], g_dsp.r.ar[3]))
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[dreg]));
else
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, dreg, dsp_increment_addr_reg(dreg));
}
writeToBackLog(3, DSP_REG_AR3,
dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
}
// LDNM $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 11ss
void ldnm(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
if (sreg != DSP_REG_AR3) {
writeToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
else
writeToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
} else {
writeToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[dreg]));
if (IsSameMemArea(g_dsp.r.ar[dreg], g_dsp.r.ar[3]))
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[dreg]));
else
writeToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
writeToBackLog(2, dreg, dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]));
}
writeToBackLog(3, DSP_REG_AR3,
dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
}
void nop(const UDSPInstruction opc)
{
}
} // end namespace ext
} // end namespace DSPInterpeter
// The ext ops are calculated in parallel with the actual op. That means that
// both the main op and the ext op see the same register state as input. The
// output is simple as long as the main and ext ops don't change the same
// register. If they do the output is the bitwise or of the result of both the
// main and ext ops.
// The ext op are writing their output into the backlog which is
// being applied to the real registers after the main op was executed
void applyWriteBackLog()
{
// always make sure to have an extra entry at the end w/ -1 to avoid
// infinitive loops
for (int i = 0; writeBackLogIdx[i] != -1; i++) {
dsp_op_write_reg(writeBackLogIdx[i], dsp_op_read_reg(writeBackLogIdx[i]) | writeBackLog[i]);
// Clear back log
writeBackLogIdx[i] = -1;
}
}
// This function is being called in the main op after all input regs were read
// and before it writes into any regs. This way we can always use bitwise or to
// apply the ext command output, because if the main op didn't change the value
// then 0 | ext output = ext output and if it did then bitwise or is still the
// right thing to do
void zeroWriteBackLog()
{
// always make sure to have an extra entry at the end w/ -1 to avoid
// infinitive loops
for (int i = 0; writeBackLogIdx[i] != -1; i++) {
dsp_op_write_reg(writeBackLogIdx[i], 0);
}
}
//needed for 0x3... cases when main and extended are modifying the same ACC
//games are not doing that + in motorola (similar dsp) dox this is forbidden to do
//ex. corner case -> 0x3060: main opcode modifies .m, and extended .l -> .l shoudnt be zeroed because of .m write...
void zeroWriteBackLogPreserveAcc(u8 acc)
{
for (int i = 0; writeBackLogIdx[i] != -1; i++) {
// acc0
if ((acc == 0) &&
((writeBackLogIdx[i] == DSP_REG_ACL0) || (writeBackLogIdx[i] == DSP_REG_ACM0) || (writeBackLogIdx[i] == DSP_REG_ACH0)))
continue;
// acc1
if ((acc == 1) &&
((writeBackLogIdx[i] == DSP_REG_ACL1) || (writeBackLogIdx[i] == DSP_REG_ACM1) || (writeBackLogIdx[i] == DSP_REG_ACH1)))
continue;
dsp_op_write_reg(writeBackLogIdx[i], 0);
}
}

View File

@ -0,0 +1,64 @@
/*====================================================================
filename: opcodes.h
project: GameCube DSP Tool (gcdsp)
created: 2005.03.04
mail: duddie@walla.com
Copyright (c) 2005 Duddie
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.
====================================================================*/
#ifndef _GDSP_EXT_OP_H
#define _GDSP_EXT_OP_H
#include "DSPTables.h"
// Extended opcode support.
// Many opcode have the lower 0xFF (some only 0x7f) free - there, an opcode extension
// can be stored.
namespace DSPInterpreter
{
namespace Ext
{
void l(const UDSPInstruction opc);
void ln(const UDSPInstruction opc);
void ls(const UDSPInstruction opc);
void lsn(const UDSPInstruction opc);
void lsm(const UDSPInstruction opc);
void lsnm(const UDSPInstruction opc);
void sl(const UDSPInstruction opc);
void sln(const UDSPInstruction opc);
void slm(const UDSPInstruction opc);
void slnm(const UDSPInstruction opc);
void s(const UDSPInstruction opc);
void sn(const UDSPInstruction opc);
void ld(const UDSPInstruction opc);
void ldn(const UDSPInstruction opc);
void ldm(const UDSPInstruction opc);
void ldnm(const UDSPInstruction opc);
void mv(const UDSPInstruction opc);
void dr(const UDSPInstruction opc);
void ir(const UDSPInstruction opc);
void nr(const UDSPInstruction opc);
void nop(const UDSPInstruction opc);
} // end namespace Ext
} // end namespace DSPinterpeter
#endif

View File

@ -0,0 +1,386 @@
/*====================================================================
filename: gdsp_opcodes_helper.h
project: GameCube DSP Tool (gcdsp)
created: 2005.03.04
mail: duddie@walla.com
Copyright (c) 2005 Duddie
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.
====================================================================*/
#ifndef _DSP_INT_UTIL_H
#define _DSP_INT_UTIL_H
#include "Common.h"
#include "DSPInterpreter.h"
#include "DSPCore.h"
#include "DSPMemoryMap.h"
#include "DSPStacks.h"
// ---------------------------------------------------------------------------------------
// --- SR
// ---------------------------------------------------------------------------------------
inline void dsp_SR_set_flag(int flag)
{
g_dsp.r.sr |= flag;
}
inline bool dsp_SR_is_flag_set(int flag)
{
return (g_dsp.r.sr & flag) != 0;
}
// ---------------------------------------------------------------------------------------
// --- AR increments, decrements
// ---------------------------------------------------------------------------------------
inline u16 dsp_increase_addr_reg(u16 reg, s16 _ix)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
s32 ix = _ix;
u32 mx = (wr | 1) << 1;
u32 nar = ar + ix;
u32 dar = (nar ^ ar ^ ix) & mx;
if (ix >= 0)
{
if (dar > wr) //overflow
nar -= wr + 1;
}
else
{
if ((((nar + wr + 1) ^ nar) & dar) <= wr) //underflow or below min for mask
nar += wr + 1;
}
return nar;
}
inline u16 dsp_decrease_addr_reg(u16 reg, s16 _ix)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
s32 ix = _ix;
u32 mx = (wr | 1) << 1;
u32 nar = ar - ix;
u32 dar = (nar ^ ar ^ ~ix) & mx;
if ((u32)ix > 0xFFFF8000) //(ix < 0 && ix != -0x8000)
{
if (dar > wr) //overflow
nar -= wr + 1;
}
else
{
if ((((nar + wr + 1) ^ nar) & dar) <= wr) //underflow or below min for mask
nar += wr + 1;
}
return nar;
}
inline u16 dsp_increment_addr_reg(u16 reg)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
u32 nar = ar + 1;
if ((nar ^ ar) > ((wr | 1) << 1))
nar -= wr + 1;
return nar;
}
inline u16 dsp_decrement_addr_reg(u16 reg)
{
u32 ar = g_dsp.r.ar[reg];
u32 wr = g_dsp.r.wr[reg];
u32 nar = ar + wr;
if (((nar ^ ar) & ((wr | 1) << 1)) > wr)
nar -= wr + 1;
return nar;
}
// ---------------------------------------------------------------------------------------
// --- reg
// ---------------------------------------------------------------------------------------
inline u16 dsp_op_read_reg(int reg)
{
switch (reg & 0x1f) {
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
return dsp_reg_load_stack(reg - DSP_REG_ST0);
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
return g_dsp.r.ar[reg - DSP_REG_AR0];
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
return g_dsp.r.ix[reg - DSP_REG_IX0];
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
return g_dsp.r.wr[reg - DSP_REG_WR0];
case DSP_REG_ACH0:
case DSP_REG_ACH1:
return g_dsp.r.ac[reg - DSP_REG_ACH0].h;
case DSP_REG_CR: return g_dsp.r.cr;
case DSP_REG_SR: return g_dsp.r.sr;
case DSP_REG_PRODL: return g_dsp.r.prod.l;
case DSP_REG_PRODM: return g_dsp.r.prod.m;
case DSP_REG_PRODH: return g_dsp.r.prod.h;
case DSP_REG_PRODM2: return g_dsp.r.prod.m2;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
return g_dsp.r.ax[reg - DSP_REG_AXL0].l;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
return g_dsp.r.ax[reg - DSP_REG_AXH0].h;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
return g_dsp.r.ac[reg - DSP_REG_ACL0].l;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
return g_dsp.r.ac[reg - DSP_REG_ACM0].m;
default:
_assert_msg_(DSP_INT, 0, "cannot happen");
return 0;
}
}
inline void dsp_op_write_reg(int reg, u16 val)
{
switch (reg & 0x1f) {
// 8-bit sign extended registers. Should look at prod.h too...
case DSP_REG_ACH0:
case DSP_REG_ACH1:
// sign extend from the bottom 8 bits.
g_dsp.r.ac[reg-DSP_REG_ACH0].h = (u16)(s16)(s8)(u8)val;
break;
// Stack registers.
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
dsp_reg_store_stack(reg - DSP_REG_ST0, val);
break;
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
g_dsp.r.ar[reg - DSP_REG_AR0] = val;
break;
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
g_dsp.r.ix[reg - DSP_REG_IX0] = val;
break;
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
g_dsp.r.wr[reg - DSP_REG_WR0] = val;
break;
case DSP_REG_CR: g_dsp.r.cr = val; break;
case DSP_REG_SR: g_dsp.r.sr = val; break;
case DSP_REG_PRODL: g_dsp.r.prod.l = val; break;
case DSP_REG_PRODM: g_dsp.r.prod.m = val; break;
case DSP_REG_PRODH: g_dsp.r.prod.h = val; break;
case DSP_REG_PRODM2: g_dsp.r.prod.m2 = val; break;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
g_dsp.r.ax[reg - DSP_REG_AXL0].l = val;
break;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
g_dsp.r.ax[reg - DSP_REG_AXH0].h = val;
break;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
g_dsp.r.ac[reg - DSP_REG_ACL0].l = val;
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
g_dsp.r.ac[reg - DSP_REG_ACM0].m = val;
break;
}
}
inline void dsp_conditional_extend_accum(int reg)
{
switch (reg)
{
case DSP_REG_ACM0:
case DSP_REG_ACM1:
if (g_dsp.r.sr & SR_40_MODE_BIT)
{
// Sign extend into whole accum.
u16 val = g_dsp.r.ac[reg-DSP_REG_ACM0].m;
g_dsp.r.ac[reg - DSP_REG_ACM0].h = (val & 0x8000) ? 0xFFFF : 0x0000;
g_dsp.r.ac[reg - DSP_REG_ACM0].l = 0;
}
}
}
// ---------------------------------------------------------------------------------------
// --- prod
// ---------------------------------------------------------------------------------------
inline s64 dsp_get_long_prod()
{
#if PROFILE
ProfilerAddDelta(g_dsp.err_pc, 1);
#endif
s64 val = (s8)(u8)g_dsp.r.prod.h;
val <<= 32;
s64 low_prod = g_dsp.r.prod.m;
low_prod += g_dsp.r.prod.m2;
low_prod <<= 16;
low_prod |= g_dsp.r.prod.l;
val += low_prod;
return val;
}
inline s64 dsp_get_long_prod_round_prodl()
{
s64 prod = dsp_get_long_prod();
if (prod & 0x10000)
prod = (prod + 0x8000) & ~0xffff;
else
prod = (prod + 0x7fff) & ~0xffff;
return prod;
}
// For accurate emulation, this is wrong - but the real prod registers behave
// in completely bizarre ways. Not needed to emulate them correctly for game ucodes.
inline void dsp_set_long_prod(s64 val)
{
#if PROFILE
ProfilerAddDelta(g_dsp.err_pc, 1);
#endif
g_dsp.r.prod.l = (u16)val;
val >>= 16;
g_dsp.r.prod.m = (u16)val;
val >>= 16;
g_dsp.r.prod.h = /*(s16)(s8)*/(u8)val;//todo: check expansion
g_dsp.r.prod.m2 = 0;
}
// ---------------------------------------------------------------------------------------
// --- ACC - main accumulators (40-bit)
// ---------------------------------------------------------------------------------------
inline s64 dsp_get_long_acc(int reg)
{
#if PROFILE
ProfilerAddDelta(g_dsp.err_pc, 1);
#endif
s64 high = (s64)(s8)g_dsp.r.ac[reg].h << 32;
u32 mid_low = ((u32)g_dsp.r.ac[reg].m << 16) | g_dsp.r.ac[reg].l;
return high | mid_low;
}
inline void dsp_set_long_acc(int _reg, s64 val)
{
#if PROFILE
ProfilerAddDelta(g_dsp.err_pc, 1);
#endif
g_dsp.r.ac[_reg].l = (u16)val;
val >>= 16;
g_dsp.r.ac[_reg].m = (u16)val;
val >>= 16;
g_dsp.r.ac[_reg].h = (u16)(s16)(s8)(u8)val;
}
inline s64 dsp_convert_long_acc(s64 val) // s64 -> s40
{
return ((s64)(s8)(val >> 32))<<32 | (u32)val;
}
inline s64 dsp_round_long_acc(s64 val)
{
if (val & 0x10000)
val = (val + 0x8000) & ~0xffff;
else
val = (val + 0x7fff) & ~0xffff;
return val;
}
inline s16 dsp_get_acc_l(int _reg)
{
return g_dsp.r.ac[_reg].l;
}
inline s16 dsp_get_acc_m(int _reg)
{
return g_dsp.r.ac[_reg].m;
}
inline s16 dsp_get_acc_h(int _reg)
{
return g_dsp.r.ac[_reg].h;
}
// ---------------------------------------------------------------------------------------
// --- AX - extra accumulators (32-bit)
// ---------------------------------------------------------------------------------------
inline s32 dsp_get_long_acx(int _reg)
{
#if PROFILE
ProfilerAddDelta(g_dsp.err_pc, 1);
#endif
return ((u32)g_dsp.r.ax[_reg].h << 16) | g_dsp.r.ax[_reg].l;
}
inline s16 dsp_get_ax_l(int _reg)
{
return (s16)g_dsp.r.ax[_reg].l;
}
inline s16 dsp_get_ax_h(int _reg)
{
return (s16)g_dsp.r.ax[_reg].h;
}
#endif

View File

@ -0,0 +1,261 @@
/*====================================================================
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 "DSPTables.h"
#include "DSPHost.h"
#include "DSPCore.h"
#include "DSPAnalyzer.h"
#include "DSPHWInterface.h"
#include "DSPIntUtil.h"
namespace DSPInterpreter {
volatile u32 gdsp_running;
// NOTE: These have nothing to do with g_dsp.r[DSP_REG_CR].
// Hm, should instructions that change CR use this? Probably not (but they
// should call UpdateCachedCR())
void WriteCR(u16 val)
{
// reset
if (val & 1)
{
DSPCore_Reset();
val &= ~1;
}
// init - can reset and init be done at the same time?
else if (val == 4)
{
// this looks like a hack! OSInitAudioSystem ucode
// should send this mail - not dsp core itself
gdsp_mbox_write_h(GDSP_MBOX_DSP, 0x8054);
gdsp_mbox_write_l(GDSP_MBOX_DSP, 0x4348);
val |= 0x800;
}
// update cr
g_dsp.cr = val;
}
// Hm, should instructions that read CR use this? (Probably not).
u16 ReadCR()
{
if (g_dsp.pc & 0x8000)
{
g_dsp.cr |= 0x800;
}
else
{
g_dsp.cr &= ~0x800;
}
return g_dsp.cr;
}
void Step()
{
DSPCore_CheckExceptions();
g_dsp.step_counter++;
#if PROFILE
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
u16 opc = dsp_fetch_code();
ExecuteInstruction(UDSPInstruction(opc));
if (DSPAnalyzer::code_flags[g_dsp.pc - 1] & DSPAnalyzer::CODE_LOOP_END)
HandleLoop();
}
// Used by thread mode.
//void Run()
//{
// int checkInterrupt = 0;
// gdsp_running = true;
// while (!(g_dsp.cr & CR_HALT) && gdsp_running)
// {
// if (jit)
// jit->RunForCycles(1);
// else {
// // Automatically let the other threads work if we're idle skipping
// if(DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
// Common::YieldCPU();
//
// Step();
//
// // Turns out the less you check for external interrupts, the more
// // sound you hear, and it becomes slower
// checkInterrupt++;
// if(checkInterrupt == 500) { // <-- Arbitrary number. TODO: tweak
// DSPCore_CheckExternalInterrupt();
// checkInterrupt = 0;
// }
// }
// }
// gdsp_running = false;
//}
// This one has basic idle skipping, and checks breakpoints.
int RunCyclesDebug(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 (g_dsp.cr & CR_HALT)
return 0;
if (dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
{
DSPCore_SetState(DSPCORE_STEPPING);
return cycles;
}
Step();
cycles--;
if (cycles < 0)
return 0;
}
// In thread mode, process external interrupts
if (g_dsp.external_interrupt_waiting)
{
DSPCore_CheckExternalInterrupt();
DSPCore_CheckExceptions();
DSPCore_SetExternalInterrupt(false);
}
while (true)
{
// Next, let's run a few cycles with idle skipping, so that we can skip
// idle loops.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
if (dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
{
DSPCore_SetState(DSPCORE_STEPPING);
return cycles;
}
// Idle skipping.
if (DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
// Now, lets run some more without idle skipping.
for (int i = 0; i < 200; i++)
{
if (dsp_breakpoints.IsAddressBreakPoint(g_dsp.pc))
{
DSPCore_SetState(DSPCORE_STEPPING);
return cycles;
}
Step();
cycles--;
if (cycles < 0)
return 0;
// We don't bother directly supporting pause - if the main emu pauses,
// it just won't call this function anymore.
}
}
}
// Used by non-thread mode. Meant to be efficient.
int RunCycles(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 (g_dsp.cr & CR_HALT)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
// In thread mode, process external interrupts
if (g_dsp.external_interrupt_waiting)
{
DSPCore_CheckExternalInterrupt();
DSPCore_CheckExceptions();
DSPCore_SetExternalInterrupt(false);
}
while (true)
{
// Next, let's run a few cycles with idle skipping, so that we can skip
// idle loops.
for (int i = 0; i < 8; i++)
{
if (g_dsp.cr & CR_HALT)
return 0;
// Idle skipping.
if (DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
return 0;
Step();
cycles--;
if (cycles < 0)
return 0;
}
// Now, lets run some more without idle skipping.
for (int i = 0; i < 200; i++)
{
Step();
cycles--;
if (cycles < 0)
return 0;
// We don't bother directly supporting pause - if the main emu pauses,
// it just won't call this function anymore.
}
}
}
void Stop()
{
gdsp_running = false;
}
} // namespace

View File

@ -0,0 +1,170 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _DSPINTERPRETER_H
#define _DSPINTERPRETER_H
#include "DSPTables.h"
#define DSP_REG_MASK 0x1f
namespace DSPInterpreter {
void Step();
void Run();
// See: DspIntBranch.cpp
void HandleLoop();
// If these simply return the same number of cycles as was passed into them,
// chances are that the DSP is halted.
// The difference between them is that the debug one obeys breakpoints.
int RunCycles(int cycles);
int RunCyclesDebug(int cycles);
void Stop();
void WriteCR(u16 val);
u16 ReadCR();
typedef void (*DSPInterpreterFunc)(const UDSPInstruction opc);
// All the opcode functions.
void unknown(const UDSPInstruction opc);
void call(const UDSPInstruction opc);
void callr(const UDSPInstruction opc);
void ifcc(const UDSPInstruction opc);
void jcc(const UDSPInstruction opc);
void jmprcc(const UDSPInstruction opc);
void ret(const UDSPInstruction opc);
void halt(const UDSPInstruction opc);
void loop(const UDSPInstruction opc);
void loopi(const UDSPInstruction opc);
void bloop(const UDSPInstruction opc);
void bloopi(const UDSPInstruction opc);
void mrr(const UDSPInstruction opc);
void lrr(const UDSPInstruction opc);
void lrrd(const UDSPInstruction opc);
void lrri(const UDSPInstruction opc);
void lrrn(const UDSPInstruction opc);
void srr(const UDSPInstruction opc);
void srrd(const UDSPInstruction opc);
void srri(const UDSPInstruction opc);
void srrn(const UDSPInstruction opc);
void lri(const UDSPInstruction opc);
void lris(const UDSPInstruction opc);
void lr(const UDSPInstruction opc);
void sr(const UDSPInstruction opc);
void si(const UDSPInstruction opc);
void tstaxh(const UDSPInstruction opc);
void clr(const UDSPInstruction opc);
void clrl(const UDSPInstruction opc);
void clrp(const UDSPInstruction opc);
void mulc(const UDSPInstruction opc);
void cmpar(const UDSPInstruction opc);
void cmp(const UDSPInstruction opc);
void tst(const UDSPInstruction opc);
void addaxl(const UDSPInstruction opc);
void addarn(const UDSPInstruction opc);
void mulcac(const UDSPInstruction opc);
void movr(const UDSPInstruction opc);
void movax(const UDSPInstruction opc);
void xorr(const UDSPInstruction opc);
void andr(const UDSPInstruction opc);
void orr(const UDSPInstruction opc);
void andc(const UDSPInstruction opc);
void orc(const UDSPInstruction opc);
void xorc(const UDSPInstruction opc);
void notc(const UDSPInstruction opc);
void lsrnrx(const UDSPInstruction opc);
void asrnrx(const UDSPInstruction opc);
void lsrnr(const UDSPInstruction opc);
void asrnr(const UDSPInstruction opc);
void add(const UDSPInstruction opc);
void addp(const UDSPInstruction opc);
void cmpis(const UDSPInstruction opc);
void addpaxz(const UDSPInstruction opc);
void movpz(const UDSPInstruction opc);
void decm(const UDSPInstruction opc);
void dec(const UDSPInstruction opc);
void inc(const UDSPInstruction opc);
void incm(const UDSPInstruction opc);
void neg(const UDSPInstruction opc);
void addax(const UDSPInstruction opc);
void addr(const UDSPInstruction opc);
void subr(const UDSPInstruction opc);
void subp(const UDSPInstruction opc);
void subax(const UDSPInstruction opc);
void addis(const UDSPInstruction opc);
void addi(const UDSPInstruction opc);
void lsl16(const UDSPInstruction opc);
void madd(const UDSPInstruction opc);
void msub(const UDSPInstruction opc);
void lsr16(const UDSPInstruction opc);
void asr16(const UDSPInstruction opc);
void lsl(const UDSPInstruction opc);
void lsr(const UDSPInstruction opc);
void asl(const UDSPInstruction opc);
void asr(const UDSPInstruction opc);
void lsrn(const UDSPInstruction opc);
void asrn(const UDSPInstruction opc);
void dar(const UDSPInstruction opc);
void iar(const UDSPInstruction opc);
void subarn(const UDSPInstruction opc);
void sbclr(const UDSPInstruction opc);
void sbset(const UDSPInstruction opc);
void mov(const UDSPInstruction opc);
void movp(const UDSPInstruction opc);
void mul(const UDSPInstruction opc);
void mulac(const UDSPInstruction opc);
void mulmv(const UDSPInstruction opc);
void mulmvz(const UDSPInstruction opc);
void mulx(const UDSPInstruction opc);
void mulxac(const UDSPInstruction opc);
void mulxmv(const UDSPInstruction opc);
void mulxmvz(const UDSPInstruction opc);
void mulcmvz(const UDSPInstruction opc);
void mulcmv(const UDSPInstruction opc);
void movnp(const UDSPInstruction opc);
void sub(const UDSPInstruction opc);
void maddx(const UDSPInstruction opc);
void msubx(const UDSPInstruction opc);
void maddc(const UDSPInstruction opc);
void msubc(const UDSPInstruction opc);
void srs(const UDSPInstruction opc);
void lrs(const UDSPInstruction opc);
void nx(const UDSPInstruction opc);
void cmpi(const UDSPInstruction opc);
void rti(const UDSPInstruction opc);
void ilrr(const UDSPInstruction opc);
void ilrrd(const UDSPInstruction opc);
void ilrri(const UDSPInstruction opc);
void ilrrn(const UDSPInstruction opc);
void andcf(const UDSPInstruction opc);
void andf(const UDSPInstruction opc);
void xori(const UDSPInstruction opc);
void andi(const UDSPInstruction opc);
void ori(const UDSPInstruction opc);
void srbith(const UDSPInstruction opc);
void mulaxh(const UDSPInstruction opc);
void tstprod(const UDSPInstruction opc);
void abs(const UDSPInstruction opc);
} // namespace
#endif // _DSPINTERPRETER_H

View File

@ -0,0 +1,83 @@
/*====================================================================
filename: gdsp_memory.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 "DSPInterpreter.h"
#include "DSPMemoryMap.h"
#include "DSPHWInterface.h"
#include "DSPCore.h"
u16 dsp_imem_read(u16 addr)
{
switch (addr >> 12)
{
case 0: // 0xxx IRAM
return g_dsp.iram[addr & DSP_IRAM_MASK];
case 8: // 8xxx IROM - contains code to receive code for IRAM, and a bunch of mixing loops.
return g_dsp.irom[addr & DSP_IROM_MASK];
default: // Unmapped/non-existing memory
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Executing from invalid (%04x) memory", g_dsp.pc, addr);
return 0;
}
}
u16 dsp_dmem_read(u16 addr)
{
switch (addr >> 12)
{
case 0x0: // 0xxx DRAM
return g_dsp.dram[addr & DSP_DRAM_MASK];
case 0x1: // 1xxx COEF
DEBUG_LOG(DSPLLE, "%04x : Coef Read @ %04x", g_dsp.pc, addr);
return g_dsp.coef[addr & DSP_COEF_MASK];
case 0xf: // Fxxx HW regs
return gdsp_ifx_read(addr);
default: // Unmapped/non-existing memory
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Read from UNKNOWN (%04x) memory", g_dsp.pc, addr);
return 0;
}
}
void dsp_dmem_write(u16 addr, u16 val)
{
switch (addr >> 12)
{
case 0x0: // 0xxx DRAM
g_dsp.dram[addr & DSP_DRAM_MASK] = val;
break;
case 0xf: // Fxxx HW regs
gdsp_ifx_write(addr, val);
break;
default: // Unmapped/non-existing memory
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Write to UNKNOWN (%04x) memory", g_dsp.pc, addr);
break;
}
}

View File

@ -0,0 +1,55 @@
/*====================================================================
filename: gdsp_memory.h
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.
====================================================================*/
#ifndef _GDSP_MEMORY_H
#define _GDSP_MEMORY_H
#include "Common.h"
#include "DSPInterpreter.h"
#include "DSPCore.h"
u16 dsp_imem_read(u16 addr);
void dsp_dmem_write(u16 addr, u16 val);
u16 dsp_dmem_read(u16 addr);
inline u16 dsp_fetch_code()
{
u16 opc = dsp_imem_read(g_dsp.pc);
g_dsp.pc++;
return opc;
}
inline u16 dsp_peek_code()
{
return dsp_imem_read(g_dsp.pc);
}
inline void dsp_skip_inst()
{
g_dsp.pc += opTable[dsp_peek_code()]->size;
}
#endif

View File

@ -0,0 +1,58 @@
/*====================================================================
filename: gdsp_registers.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 "Common.h"
#include "DSPCore.h"
#include "DSPStacks.h"
// Stacks. The stacks are outside the DSP RAM, in dedicated hardware.
void dsp_reg_stack_push(int stack_reg)
{
g_dsp.reg_stack_ptr[stack_reg]++;
g_dsp.reg_stack_ptr[stack_reg] &= DSP_STACK_MASK;
g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]] = g_dsp.r.st[stack_reg];
}
void dsp_reg_stack_pop(int stack_reg)
{
g_dsp.r.st[stack_reg] = g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]];
g_dsp.reg_stack_ptr[stack_reg]--;
g_dsp.reg_stack_ptr[stack_reg] &= DSP_STACK_MASK;
}
void dsp_reg_store_stack(int stack_reg, u16 val)
{
dsp_reg_stack_push(stack_reg);
g_dsp.r.st[stack_reg] = val;
}
u16 dsp_reg_load_stack(int stack_reg)
{
u16 val = g_dsp.r.st[stack_reg];
dsp_reg_stack_pop(stack_reg);
return val;
}

View File

@ -0,0 +1,34 @@
/*====================================================================
filename: gdsp_registers.h
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.
====================================================================*/
#ifndef _DSP_STACKS_H
#define _DSP_STACKS_H
#include "Common.h"
void dsp_reg_store_stack(int stack_reg, u16 val);
u16 dsp_reg_load_stack(int stack_reg);
#endif

View File

@ -0,0 +1,568 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie (c) 2005 (duddie@walla.com)
#include "Common.h"
#include "DSPTables.h"
#include "DSPInterpreter.h"
#include "DSPIntExtOps.h"
#include "DSPEmitter.h"
void nop(const UDSPInstruction opc)
{
// The real nop is 0. Anything else is bad.
if (opc)
DSPInterpreter::unknown(opc);
}
const DSPOPCTemplate opcodes[] =
{
{"NOP", 0x0000, 0xfffc, nop, &DSPEmitter::nop, 1, 0, {}, false, false, false, false, false},
{"DAR", 0x0004, 0xfffc, DSPInterpreter::dar, &DSPEmitter::dar, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
{"IAR", 0x0008, 0xfffc, DSPInterpreter::iar, &DSPEmitter::iar, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
{"SUBARN", 0x000c, 0xfffc, DSPInterpreter::subarn, &DSPEmitter::subarn, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
{"ADDARN", 0x0010, 0xfff0, DSPInterpreter::addarn, &DSPEmitter::addarn, 1, 2, {{P_REG, 1, 0, 0, 0x0003}, {P_REG04, 1, 0, 2, 0x000c}}, false, false, false, false, false},
{"HALT", 0x0021, 0xffff, DSPInterpreter::halt, &DSPEmitter::halt, 1, 0, {}, false, true, true, false, false},
{"RETGE", 0x02d0, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETL", 0x02d1, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETG", 0x02d2, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETLE", 0x02d3, 0xffff, DSPInterpreter::ret, NULL/*&DSPEmitter::ret*/, 1, 0, {}, false, true, false, true, false},
{"RETNZ", 0x02d4, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETZ", 0x02d5, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETNC", 0x02d6, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETC", 0x02d7, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETx8", 0x02d8, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETx9", 0x02d9, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETxA", 0x02da, 0xffff, DSPInterpreter::ret, NULL/*&DSPEmitter::ret*/, 1, 0, {}, false, true, false, true, false},
{"RETxB", 0x02db, 0xffff, DSPInterpreter::ret, NULL/*&DSPEmitter::ret*/, 1, 0, {}, false, true, false, true, false},
{"RETLNZ", 0x02dc, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETLZ", 0x02dd, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RETO", 0x02de, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, false, true, false},
{"RET", 0x02df, 0xffff, DSPInterpreter::ret, &DSPEmitter::ret, 1, 0, {}, false, true, true, false, false},
{"RTI", 0x02ff, 0xffff, DSPInterpreter::rti, &DSPEmitter::rti, 1, 0, {}, false, true, true, false, false},
{"CALLGE", 0x02b0, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLL", 0x02b1, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLG", 0x02b2, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLLE", 0x02b3, 0xffff, DSPInterpreter::call, NULL/*&DSPEmitter::call*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLNZ", 0x02b4, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLZ", 0x02b5, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLNC", 0x02b6, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLC", 0x02b7, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLx8", 0x02b8, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLx9", 0x02b9, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLxA", 0x02ba, 0xffff, DSPInterpreter::call, NULL/*&DSPEmitter::call*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLxB", 0x02bb, 0xffff, DSPInterpreter::call, NULL/*&DSPEmitter::call*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLLNZ", 0x02bc, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLLZ", 0x02bd, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALLO", 0x02be, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"CALL", 0x02bf, 0xffff, DSPInterpreter::call, &DSPEmitter::call, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, true, true, false},
{"IFGE", 0x0270, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFL", 0x0271, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFG", 0x0272, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFLE", 0x0273, 0xffff, DSPInterpreter::ifcc, NULL/*&DSPEmitter::ifcc*/, 1, 0, {}, false, true, false, true, false},
{"IFNZ", 0x0274, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFZ", 0x0275, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFNC", 0x0276, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFC", 0x0277, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFx8", 0x0278, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFx9", 0x0279, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFxA", 0x027a, 0xffff, DSPInterpreter::ifcc, NULL/*&DSPEmitter::ifcc*/, 1, 0, {}, false, true, false, true, false},
{"IFxB", 0x027b, 0xffff, DSPInterpreter::ifcc, NULL/*&DSPEmitter::ifcc*/, 1, 0, {}, false, true, false, true, false},
{"IFLNZ", 0x027c, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFLZ", 0x027d, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IFO", 0x027e, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, false, true, false},
{"IF", 0x027f, 0xffff, DSPInterpreter::ifcc, &DSPEmitter::ifcc, 1, 0, {}, false, true, true, true, false},
{"JGE", 0x0290, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JL", 0x0291, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JG", 0x0292, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JLE", 0x0293, 0xffff, DSPInterpreter::jcc, NULL/*&DSPEmitter::jcc*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JNZ", 0x0294, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JZ", 0x0295, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JNC", 0x0296, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JC", 0x0297, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JMPx8", 0x0298, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JMPx9", 0x0299, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JMPxA", 0x029a, 0xffff, DSPInterpreter::jcc, NULL/*&DSPEmitter::jcc*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JMPxB", 0x029b, 0xffff, DSPInterpreter::jcc, NULL/*&DSPEmitter::jcc*/, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JLNZ", 0x029c, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JLZ", 0x029d, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JO", 0x029e, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"JMP", 0x029f, 0xffff, DSPInterpreter::jcc, &DSPEmitter::jcc, 2, 1, {{P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, true, true, false},
{"JRGE", 0x1700, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRL", 0x1701, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRG", 0x1702, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRLE", 0x1703, 0xff1f, DSPInterpreter::jmprcc, NULL/*&DSPEmitter::jmprcc*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRNZ", 0x1704, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRZ", 0x1705, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRNC", 0x1706, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRC", 0x1707, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JMPRx8", 0x1708, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JMPRx9", 0x1709, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JMPRxA", 0x170a, 0xff1f, DSPInterpreter::jmprcc, NULL/*&DSPEmitter::jmprcc*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JMPRxB", 0x170b, 0xff1f, DSPInterpreter::jmprcc, NULL/*&DSPEmitter::jmprcc*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRLNZ", 0x170c, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRLZ", 0x170d, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JRO", 0x170e, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, false, false},
{"JMPR", 0x170f, 0xff1f, DSPInterpreter::jmprcc, &DSPEmitter::jmprcc, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, true, false, false},
{"CALLRGE", 0x1710, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRL", 0x1711, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRG", 0x1712, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRLE", 0x1713, 0xff1f, DSPInterpreter::callr, NULL/*&DSPEmitter::callr*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRNZ", 0x1714, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRZ", 0x1715, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRNC", 0x1716, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRC", 0x1717, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRx8", 0x1718, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRx9", 0x1719, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRxA", 0x171a, 0xff1f, DSPInterpreter::callr, NULL/*&DSPEmitter::callr*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRxB", 0x171b, 0xff1f, DSPInterpreter::callr, NULL/*&DSPEmitter::callr*/, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRLNZ",0x171c, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRLZ", 0x171d, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLRO", 0x171e, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, false, true, false},
{"CALLR", 0x171f, 0xff1f, DSPInterpreter::callr, &DSPEmitter::callr, 1, 1, {{P_REG, 1, 0, 5, 0x00e0}}, false, true, true, true, false},
{"SBCLR", 0x1200, 0xff00, DSPInterpreter::sbclr, &DSPEmitter::sbclr, 1, 1, {{P_IMM, 1, 0, 0, 0x0007}}, false, false, false, false, false},
{"SBSET", 0x1300, 0xff00, DSPInterpreter::sbset, &DSPEmitter::sbset, 1, 1, {{P_IMM, 1, 0, 0, 0x0007}}, false, false, false, false, false},
{"LSL", 0x1400, 0xfec0, DSPInterpreter::lsl, &DSPEmitter::lsl, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x003f}}, false, false, false, false, true},
{"LSR", 0x1440, 0xfec0, DSPInterpreter::lsr, &DSPEmitter::lsr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x003f}}, false, false, false, false, true},
{"ASL", 0x1480, 0xfec0, DSPInterpreter::asl, &DSPEmitter::asl, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x003f}}, false, false, false, false, true},
{"ASR", 0x14c0, 0xfec0, DSPInterpreter::asr, &DSPEmitter::asr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x003f}}, false, false, false, false, true},
{"LSRN", 0x02ca, 0xffff, DSPInterpreter::lsrn, &DSPEmitter::lsrn, 1, 0, {}, false, false, false, false, true}, // discovered by ector!
{"ASRN", 0x02cb, 0xffff, DSPInterpreter::asrn, &DSPEmitter::asrn, 1, 0, {}, false, false, false, false, true}, // discovered by ector!
{"LRI", 0x0080, 0xffe0, DSPInterpreter::lri, &DSPEmitter::lri, 2, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, false},
{"LR", 0x00c0, 0xffe0, DSPInterpreter::lr, &DSPEmitter::lr, 2, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_MEM, 2, 1, 0, 0xffff}}, false, false, false, true, false},
{"SR", 0x00e0, 0xffe0, DSPInterpreter::sr, &DSPEmitter::sr, 2, 2, {{P_MEM, 2, 1, 0, 0xffff}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, true, false},
{"MRR", 0x1c00, 0xfc00, DSPInterpreter::mrr, &DSPEmitter::mrr, 1, 2, {{P_REG, 1, 0, 5, 0x03e0}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
{"SI", 0x1600, 0xff00, DSPInterpreter::si, &DSPEmitter::si, 2, 2, {{P_MEM, 1, 0, 0, 0x00ff}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, false},
{"ADDIS", 0x0400, 0xfe00, DSPInterpreter::addis, &DSPEmitter::addis, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x00ff}}, false, false, false, false, true},
{"CMPIS", 0x0600, 0xfe00, DSPInterpreter::cmpis, &DSPEmitter::cmpis, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 1, 0, 0, 0x00ff}}, false, false, false, false, true},
{"LRIS", 0x0800, 0xf800, DSPInterpreter::lris, &DSPEmitter::lris, 1, 2, {{P_REG18, 1, 0, 8, 0x0700}, {P_IMM, 1, 0, 0, 0x00ff}}, false, false, false, false, true},
{"ADDI", 0x0200, 0xfeff, DSPInterpreter::addi, &DSPEmitter::addi, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
{"XORI", 0x0220, 0xfeff, DSPInterpreter::xori, &DSPEmitter::xori, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
{"ANDI", 0x0240, 0xfeff, DSPInterpreter::andi, &DSPEmitter::andi, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
{"ORI", 0x0260, 0xfeff, DSPInterpreter::ori, &DSPEmitter::ori, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
{"CMPI", 0x0280, 0xfeff, DSPInterpreter::cmpi, &DSPEmitter::cmpi, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
{"ANDF", 0x02a0, 0xfeff, DSPInterpreter::andf, &DSPEmitter::andf, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
{"ANDCF", 0x02c0, 0xfeff, DSPInterpreter::andcf, &DSPEmitter::andcf, 2, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_IMM, 2, 1, 0, 0xffff}}, false, false, false, true, true},
{"ILRR", 0x0210, 0xfefc, DSPInterpreter::ilrr, &DSPEmitter::ilrr, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
{"ILRRD", 0x0214, 0xfefc, DSPInterpreter::ilrrd, &DSPEmitter::ilrrd, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
{"ILRRI", 0x0218, 0xfefc, DSPInterpreter::ilrri, &DSPEmitter::ilrri, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
{"ILRRN", 0x021c, 0xfefc, DSPInterpreter::ilrrn, &DSPEmitter::ilrrn, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false, false, false},
// LOOPS
{"LOOP", 0x0040, 0xffe0, DSPInterpreter::loop, &DSPEmitter::loop, 1, 1, {{P_REG, 1, 0, 0, 0x001f}}, false, true, false, true, false},
{"BLOOP", 0x0060, 0xffe0, DSPInterpreter::bloop, &DSPEmitter::bloop, 2, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
{"LOOPI", 0x1000, 0xff00, DSPInterpreter::loopi, &DSPEmitter::loopi, 1, 1, {{P_IMM, 1, 0, 0, 0x00ff}}, false, true, false, true, false},
{"BLOOPI", 0x1100, 0xff00, DSPInterpreter::bloopi, &DSPEmitter::bloopi, 2, 2, {{P_IMM, 1, 0, 0, 0x00ff}, {P_ADDR_I, 2, 1, 0, 0xffff}}, false, true, false, true, false},
// load and store value pointed by indexing reg and increment; LRR/SRR variants
{"LRR", 0x1800, 0xff80, DSPInterpreter::lrr, &DSPEmitter::lrr, 1, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_PRG, 1, 0, 5, 0x0060}}, false, false, false, false, false},
{"LRRD", 0x1880, 0xff80, DSPInterpreter::lrrd, &DSPEmitter::lrrd, 1, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_PRG, 1, 0, 5, 0x0060}}, false, false, false, false, false},
{"LRRI", 0x1900, 0xff80, DSPInterpreter::lrri, &DSPEmitter::lrri, 1, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_PRG, 1, 0, 5, 0x0060}}, false, false, false, false, false},
{"LRRN", 0x1980, 0xff80, DSPInterpreter::lrrn, &DSPEmitter::lrrn, 1, 2, {{P_REG, 1, 0, 0, 0x001f}, {P_PRG, 1, 0, 5, 0x0060}}, false, false, false, false, false},
{"SRR", 0x1a00, 0xff80, DSPInterpreter::srr, &DSPEmitter::srr, 1, 2, {{P_PRG, 1, 0, 5, 0x0060}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
{"SRRD", 0x1a80, 0xff80, DSPInterpreter::srrd, &DSPEmitter::srrd, 1, 2, {{P_PRG, 1, 0, 5, 0x0060}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
{"SRRI", 0x1b00, 0xff80, DSPInterpreter::srri, &DSPEmitter::srri, 1, 2, {{P_PRG, 1, 0, 5, 0x0060}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
{"SRRN", 0x1b80, 0xff80, DSPInterpreter::srrn, &DSPEmitter::srrn, 1, 2, {{P_PRG, 1, 0, 5, 0x0060}, {P_REG, 1, 0, 0, 0x001f}}, false, false, false, false, false},
//2
{"LRS", 0x2000, 0xf800, DSPInterpreter::lrs, &DSPEmitter::lrs, 1, 2, {{P_REG18, 1, 0, 8, 0x0700}, {P_MEM, 1, 0, 0, 0x00ff}}, false, false, false, false, false},
{"SRS", 0x2800, 0xf800, DSPInterpreter::srs, &DSPEmitter::srs, 1, 2, {{P_MEM, 1, 0, 0, 0x00ff}, {P_REG18, 1, 0, 8, 0x0700}}, false, false, false, false, false},
// opcodes that can be extended
//3 - main opcode defined by 9 bits, extension defined by last 7 bits!!
{"XORR", 0x3000, 0xfc80, DSPInterpreter::xorr, &DSPEmitter::xorr, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
{"ANDR", 0x3400, 0xfc80, DSPInterpreter::andr, &DSPEmitter::andr, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
{"ORR", 0x3800, 0xfc80, DSPInterpreter::orr, &DSPEmitter::orr, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
{"ANDC", 0x3c00, 0xfe80, DSPInterpreter::andc, &DSPEmitter::andc, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"ORC", 0x3e00, 0xfe80, DSPInterpreter::orc, &DSPEmitter::orc, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"XORC", 0x3080, 0xfe80, DSPInterpreter::xorc, &DSPEmitter::xorc, 1, 2, {{P_ACCM, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"NOT", 0x3280, 0xfe80, DSPInterpreter::notc, &DSPEmitter::notc, 1, 1, {{P_ACCM, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"LSRNRX", 0x3480, 0xfc80, DSPInterpreter::lsrnrx, &DSPEmitter::lsrnrx, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
{"ASRNRX", 0x3880, 0xfc80, DSPInterpreter::asrnrx, &DSPEmitter::asrnrx, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 9, 0x0200}}, true, false, false, false, true},
{"LSRNR", 0x3c80, 0xfe80, DSPInterpreter::lsrnr, &DSPEmitter::lsrnr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"ASRNR", 0x3e80, 0xfe80, DSPInterpreter::asrnr, &DSPEmitter::asrnr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACCM_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
//4
{"ADDR", 0x4000, 0xf800, DSPInterpreter::addr, &DSPEmitter::addr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG18, 1, 0, 9, 0x0600}}, true, false, false, false, true},
{"ADDAX", 0x4800, 0xfc00, DSPInterpreter::addax, &DSPEmitter::addax, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_AX, 1, 0, 9, 0x0200}}, true, false, false, false, true},
{"ADD", 0x4c00, 0xfe00, DSPInterpreter::add, &DSPEmitter::add, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACC_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"ADDP", 0x4e00, 0xfe00, DSPInterpreter::addp, &DSPEmitter::addp, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
//5
{"SUBR", 0x5000, 0xf800, DSPInterpreter::subr, &DSPEmitter::subr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG18, 1, 0, 9, 0x0600}}, true, false, false, false, true},
{"SUBAX", 0x5800, 0xfc00, DSPInterpreter::subax, &DSPEmitter::subax, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_AX, 1, 0, 9, 0x0200}}, true, false, false, false, true},
{"SUB", 0x5c00, 0xfe00, DSPInterpreter::sub, &DSPEmitter::sub, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACC_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"SUBP", 0x5e00, 0xfe00, DSPInterpreter::subp, &DSPEmitter::subp, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
//6
{"MOVR", 0x6000, 0xf800, DSPInterpreter::movr, &DSPEmitter::movr, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG18, 1, 0, 9, 0x0600}}, true, false, false, false, true},
{"MOVAX", 0x6800, 0xfc00, DSPInterpreter::movax, &DSPEmitter::movax, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_AX, 1, 0, 9, 0x0200}}, true, false, false, false, true},
{"MOV", 0x6c00, 0xfe00, DSPInterpreter::mov, &DSPEmitter::mov, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_ACC_D, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MOVP", 0x6e00, 0xfe00, DSPInterpreter::movp, &DSPEmitter::movp, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
//7
{"ADDAXL", 0x7000, 0xfc00, DSPInterpreter::addaxl, &DSPEmitter::addaxl, 1, 2, {{P_ACC, 1, 0, 8, 0x0100}, {P_REG18, 1, 0, 9, 0x0200}}, true, false, false, false, true},
{"INCM", 0x7400, 0xfe00, DSPInterpreter::incm, &DSPEmitter::incm, 1, 1, {{P_ACCM, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"INC", 0x7600, 0xfe00, DSPInterpreter::inc, &DSPEmitter::inc, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"DECM", 0x7800, 0xfe00, DSPInterpreter::decm, &DSPEmitter::decm, 1, 1, {{P_ACCM, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"DEC", 0x7a00, 0xfe00, DSPInterpreter::dec, &DSPEmitter::dec, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"NEG", 0x7c00, 0xfe00, DSPInterpreter::neg, &DSPEmitter::neg, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MOVNP", 0x7e00, 0xfe00, DSPInterpreter::movnp, &DSPEmitter::movnp, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
//8
{"NX", 0x8000, 0xf700, DSPInterpreter::nx, &DSPEmitter::nx, 1, 0, {}, true, false, false, false, false},
{"CLR", 0x8100, 0xf700, DSPInterpreter::clr, &DSPEmitter::clr, 1, 1, {{P_ACC, 1, 0, 11, 0x0800}}, true, false, false, false, true},
{"CMP", 0x8200, 0xff00, DSPInterpreter::cmp, &DSPEmitter::cmp, 1, 0, {}, true, false, false, false, true},
{"MULAXH", 0x8300, 0xff00, DSPInterpreter::mulaxh, &DSPEmitter::mulaxh, 1, 0, {}, true, false, false, false, true},
{"CLRP", 0x8400, 0xff00, DSPInterpreter::clrp, &DSPEmitter::clrp, 1, 0, {}, true, false, false, false, true},
{"TSTPROD", 0x8500, 0xff00, DSPInterpreter::tstprod, &DSPEmitter::tstprod,1, 0, {}, true, false, false, false, true},
{"TSTAXH", 0x8600, 0xfe00, DSPInterpreter::tstaxh, &DSPEmitter::tstaxh, 1, 1, {{P_REG1A, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"M2", 0x8a00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
{"M0", 0x8b00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
{"CLR15", 0x8c00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
{"SET15", 0x8d00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
{"SET16", 0x8e00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
{"SET40", 0x8f00, 0xff00, DSPInterpreter::srbith, &DSPEmitter::srbith, 1, 0, {}, true, false, false, false, false},
//9
{"MUL", 0x9000, 0xf700, DSPInterpreter::mul, &DSPEmitter::mul, 1, 2, {{P_REG18, 1, 0, 11, 0x0800}, {P_REG1A, 1, 0, 11, 0x0800}}, true, false, false, false, true},
{"ASR16", 0x9100, 0xf700, DSPInterpreter::asr16, &DSPEmitter::asr16, 1, 1, {{P_ACC, 1, 0, 11, 0x0800}}, true, false, false, false, true},
{"MULMVZ", 0x9200, 0xf600, DSPInterpreter::mulmvz, &DSPEmitter::mulmvz, 1, 3, {{P_REG18, 1, 0, 11, 0x0800}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MULAC", 0x9400, 0xf600, DSPInterpreter::mulac, &DSPEmitter::mulac, 1, 3, {{P_REG18, 1, 0, 11, 0x0800}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MULMV", 0x9600, 0xf600, DSPInterpreter::mulmv, &DSPEmitter::mulmv, 1, 3, {{P_REG18, 1, 0, 11, 0x0800}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
//a-b
{"MULX", 0xa000, 0xe700, DSPInterpreter::mulx, &DSPEmitter::mulx, 1, 2, {{P_REGM18, 1, 0, 11, 0x1000}, {P_REGM19, 1, 0, 10, 0x0800}}, true, false, false, false, true},
{"ABS", 0xa100, 0xf700, DSPInterpreter::abs, &DSPEmitter::abs, 1, 1, {{P_ACC, 1, 0, 11, 0x0800}}, true, false, false, false, true},
{"MULXMVZ", 0xa200, 0xe600, DSPInterpreter::mulxmvz, &DSPEmitter::mulxmvz,1, 3, {{P_REGM18, 1, 0, 11, 0x1000}, {P_REGM19, 1, 0, 10, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MULXAC", 0xa400, 0xe600, DSPInterpreter::mulxac, &DSPEmitter::mulxac, 1, 3, {{P_REGM18, 1, 0, 11, 0x1000}, {P_REGM19, 1, 0, 10, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MULXMV", 0xa600, 0xe600, DSPInterpreter::mulxmv, &DSPEmitter::mulxmv, 1, 3, {{P_REGM18, 1, 0, 11, 0x1000}, {P_REGM19, 1, 0, 10, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"TST", 0xb100, 0xf700, DSPInterpreter::tst, &DSPEmitter::tst, 1, 1, {{P_ACC, 1, 0, 11, 0x0800}}, true, false, false, false, true},
//c-d
{"MULC", 0xc000, 0xe700, DSPInterpreter::mulc, &DSPEmitter::mulc, 1, 2, {{P_ACCM, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}}, true, false, false, false, true},
{"CMPAR" , 0xc100, 0xe700, DSPInterpreter::cmpar, &DSPEmitter::cmpar, 1, 2, {{P_ACC, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}}, true, false, false, false, true},
{"MULCMVZ", 0xc200, 0xe600, DSPInterpreter::mulcmvz, &DSPEmitter::mulcmvz,1, 3, {{P_ACCM, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MULCAC", 0xc400, 0xe600, DSPInterpreter::mulcac, &DSPEmitter::mulcac, 1, 3, {{P_ACCM, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MULCMV", 0xc600, 0xe600, DSPInterpreter::mulcmv, &DSPEmitter::mulcmv, 1, 3, {{P_ACCM, 1, 0, 12, 0x1000}, {P_REG1A, 1, 0, 11, 0x0800}, {P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
//e
{"MADDX", 0xe000, 0xfc00, DSPInterpreter::maddx, &DSPEmitter::maddx, 1, 2, {{P_REGM18, 1, 0, 8, 0x0200}, {P_REGM19, 1, 0, 7, 0x0100}}, true, false, false, false, true},
{"MSUBX", 0xe400, 0xfc00, DSPInterpreter::msubx, &DSPEmitter::msubx, 1, 2, {{P_REGM18, 1, 0, 8, 0x0200}, {P_REGM19, 1, 0, 7, 0x0100}}, true, false, false, false, true},
{"MADDC", 0xe800, 0xfc00, DSPInterpreter::maddc, &DSPEmitter::maddc, 1, 2, {{P_ACCM, 1, 0, 9, 0x0200}, {P_REG19, 1, 0, 7, 0x0100}}, true, false, false, false, true},
{"MSUBC", 0xec00, 0xfc00, DSPInterpreter::msubc, &DSPEmitter::msubc, 1, 2, {{P_ACCM, 1, 0, 9, 0x0200}, {P_REG19, 1, 0, 7, 0x0100}}, true, false, false, false, true},
//f
{"LSL16", 0xf000, 0xfe00, DSPInterpreter::lsl16, &DSPEmitter::lsl16, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MADD", 0xf200, 0xfe00, DSPInterpreter::madd, &DSPEmitter::madd, 1, 2, {{P_REG18, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"LSR16", 0xf400, 0xfe00, DSPInterpreter::lsr16, &DSPEmitter::lsr16, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"MSUB", 0xf600, 0xfe00, DSPInterpreter::msub, &DSPEmitter::msub, 1, 2, {{P_REG18, 1, 0, 8, 0x0100}, {P_REG1A, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"ADDPAXZ", 0xf800, 0xfc00, DSPInterpreter::addpaxz, &DSPEmitter::addpaxz,1, 2, {{P_ACC, 1, 0, 9, 0x0200}, {P_AX, 1, 0, 8, 0x0100}}, true, false, false, false, true},
{"CLRL", 0xfc00, 0xfe00, DSPInterpreter::clrl, &DSPEmitter::clrl, 1, 1, {{P_ACCL, 1, 0, 11, 0x0800}}, true, false, false, false, true},
{"MOVPZ", 0xfe00, 0xfe00, DSPInterpreter::movpz, &DSPEmitter::movpz, 1, 1, {{P_ACC, 1, 0, 8, 0x0100}}, true, false, false, false, true},
};
const DSPOPCTemplate cw =
{"CW", 0x0000, 0x0000, NULL, NULL, 1, 1, {{P_VAL, 2, 0, 0, 0xffff}}, false, false};
// extended opcodes
const DSPOPCTemplate opcodes_ext[] =
{
{"XXX", 0x0000, 0x00fc, DSPInterpreter::Ext::nop, &DSPEmitter::nop, 1, 1, {{P_VAL, 1, 0, 0, 0x00ff}}, false, false, false},
{"DR", 0x0004, 0x00fc, DSPInterpreter::Ext::dr, &DSPEmitter::dr, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false},
{"IR", 0x0008, 0x00fc, DSPInterpreter::Ext::ir, &DSPEmitter::ir, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false},
{"NR", 0x000c, 0x00fc, DSPInterpreter::Ext::nr, &DSPEmitter::nr, 1, 1, {{P_REG, 1, 0, 0, 0x0003}}, false, false, false},
{"MV", 0x0010, 0x00f0, DSPInterpreter::Ext::mv, &DSPEmitter::mv, 1, 2, {{P_REG18, 1, 0, 2, 0x000c}, {P_REG1C, 1, 0, 0, 0x0003}}, false, false, false},
{"S", 0x0020, 0x00e4, DSPInterpreter::Ext::s, &DSPEmitter::s, 1, 2, {{P_PRG, 1, 0, 0, 0x0003}, {P_REG1C, 1, 0, 3, 0x0018}}, false, false, false},
{"SN", 0x0024, 0x00e4, DSPInterpreter::Ext::sn, &DSPEmitter::sn, 1, 2, {{P_PRG, 1, 0, 0, 0x0003}, {P_REG1C, 1, 0, 3, 0x0018}}, false, false, false},
{"L", 0x0040, 0x00c4, DSPInterpreter::Ext::l, &DSPEmitter::l, 1, 2, {{P_REG18, 1, 0, 3, 0x0038}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
{"LN", 0x0044, 0x00c4, DSPInterpreter::Ext::ln, &DSPEmitter::ln, 1, 2, {{P_REG18, 1, 0, 3, 0x0038}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
{"LS", 0x0080, 0x00ce, DSPInterpreter::Ext::ls, &DSPEmitter::ls, 1, 2, {{P_REG18, 1, 0, 4, 0x0030}, {P_ACCM, 1, 0, 0, 0x0001}}, false, false, false},
{"SL", 0x0082, 0x00ce, DSPInterpreter::Ext::sl, &DSPEmitter::sl, 1, 2, {{P_ACCM, 1, 0, 0, 0x0001}, {P_REG18, 1, 0, 4, 0x0030}}, false, false, false},
{"LSN", 0x0084, 0x00ce, DSPInterpreter::Ext::lsn, &DSPEmitter::lsn, 1, 2, {{P_REG18, 1, 0, 4, 0x0030}, {P_ACCM, 1, 0, 0, 0x0001}}, false, false, false},
{"SLN", 0x0086, 0x00ce, DSPInterpreter::Ext::sln, &DSPEmitter::sln, 1, 2, {{P_ACCM, 1, 0, 0, 0x0001}, {P_REG18, 1, 0, 4, 0x0030}}, false, false, false},
{"LSM", 0x0088, 0x00ce, DSPInterpreter::Ext::lsm, &DSPEmitter::lsm, 1, 2, {{P_REG18, 1, 0, 4, 0x0030}, {P_ACCM, 1, 0, 0, 0x0001}}, false, false, false},
{"SLM", 0x008a, 0x00ce, DSPInterpreter::Ext::slm, &DSPEmitter::slm, 1, 2, {{P_ACCM, 1, 0, 0, 0x0001}, {P_REG18, 1, 0, 4, 0x0030}}, false, false, false},
{"LSNM", 0x008c, 0x00ce, DSPInterpreter::Ext::lsnm, &DSPEmitter::lsnm, 1, 2, {{P_REG18, 1, 0, 4, 0x0030}, {P_ACCM, 1, 0, 0, 0x0001}}, false, false, false},
{"SLNM", 0x008e, 0x00ce, DSPInterpreter::Ext::slnm, &DSPEmitter::slnm, 1, 2, {{P_ACCM, 1, 0, 0, 0x0001}, {P_REG18, 1, 0, 4, 0x0030}}, false, false, false},
{"LD", 0x00c0, 0x00cc, DSPInterpreter::Ext::ld, &DSPEmitter::ld, 1, 3, {{P_REGM18, 1, 0, 4, 0x0020}, {P_REGM19, 1, 0, 3, 0x0010}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
{"LDN", 0x00c4, 0x00cc, DSPInterpreter::Ext::ldn, &DSPEmitter::ldn, 1, 3, {{P_REGM18, 1, 0, 4, 0x0020}, {P_REGM19, 1, 0, 3, 0x0010}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
{"LDM", 0x00c8, 0x00cc, DSPInterpreter::Ext::ldm, &DSPEmitter::ldm, 1, 3, {{P_REGM18, 1, 0, 4, 0x0020}, {P_REGM19, 1, 0, 3, 0x0010}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
{"LDNM", 0x00cc, 0x00cc, DSPInterpreter::Ext::ldnm, &DSPEmitter::ldnm, 1, 3, {{P_REGM18, 1, 0, 4, 0x0020}, {P_REGM19, 1, 0, 3, 0x0010}, {P_PRG, 1, 0, 0, 0x0003}}, false, false, false},
};
const int opcodes_size = sizeof(opcodes) / sizeof(DSPOPCTemplate);
const int opcodes_ext_size = sizeof(opcodes_ext) / sizeof(DSPOPCTemplate);
const pdlabel_t pdlabels[] =
{
{0xffa0, "COEF_A1_0", "COEF_A1_0",},
{0xffa1, "COEF_A2_0", "COEF_A2_0",},
{0xffa2, "COEF_A1_1", "COEF_A1_1",},
{0xffa3, "COEF_A2_1", "COEF_A2_1",},
{0xffa4, "COEF_A1_2", "COEF_A1_2",},
{0xffa5, "COEF_A2_2", "COEF_A2_2",},
{0xffa6, "COEF_A1_3", "COEF_A1_3",},
{0xffa7, "COEF_A2_3", "COEF_A2_3",},
{0xffa8, "COEF_A1_4", "COEF_A1_4",},
{0xffa9, "COEF_A2_4", "COEF_A2_4",},
{0xffaa, "COEF_A1_5", "COEF_A1_5",},
{0xffab, "COEF_A2_5", "COEF_A2_5",},
{0xffac, "COEF_A1_6", "COEF_A1_6",},
{0xffad, "COEF_A2_6", "COEF_A2_6",},
{0xffae, "COEF_A1_7", "COEF_A1_7",},
{0xffaf, "COEF_A2_7", "COEF_A2_7",},
{0xffb0, "0xffb0", 0,},
{0xffb1, "0xffb1", 0,},
{0xffb2, "0xffb2", 0,},
{0xffb3, "0xffb3", 0,},
{0xffb4, "0xffb4", 0,},
{0xffb5, "0xffb5", 0,},
{0xffb6, "0xffb6", 0,},
{0xffb7, "0xffb7", 0,},
{0xffb8, "0xffb8", 0,},
{0xffb9, "0xffb9", 0,},
{0xffba, "0xffba", 0,},
{0xffbb, "0xffbb", 0,},
{0xffbc, "0xffbc", 0,},
{0xffbd, "0xffbd", 0,},
{0xffbe, "0xffbe", 0,},
{0xffbf, "0xffbf", 0,},
{0xffc0, "0xffc0", 0,},
{0xffc1, "0xffc1", 0,},
{0xffc2, "0xffc2", 0,},
{0xffc3, "0xffc3", 0,},
{0xffc4, "0xffc4", 0,},
{0xffc5, "0xffc5", 0,},
{0xffc6, "0xffc6", 0,},
{0xffc7, "0xffc7", 0,},
{0xffc8, "0xffc8", 0,},
{0xffc9, "DSCR", "DSP DMA Control Reg",},
{0xffca, "0xffca", 0,},
{0xffcb, "DSBL", "DSP DMA Block Length",},
{0xffcc, "0xffcc", 0,},
{0xffcd, "DSPA", "DSP DMA DMEM Address",},
{0xffce, "DSMAH", "DSP DMA Mem Address H",},
{0xffcf, "DSMAL", "DSP DMA Mem Address L",},
{0xffd0, "0xffd0",0,},
{0xffd1, "SampleFormat", "SampleFormat",},
{0xffd2, "0xffd2",0,},
{0xffd3, "UnkZelda", "Unk Zelda reads/writes from/to it",},
{0xffd4, "ACSAH", "Accelerator start address H",},
{0xffd5, "ACSAL", "Accelerator start address L",},
{0xffd6, "ACEAH", "Accelerator end address H",},
{0xffd7, "ACEAL", "Accelerator end address L",},
{0xffd8, "ACCAH", "Accelerator current address H",},
{0xffd9, "ACCAL", "Accelerator current address L",},
{0xffda, "pred_scale", "pred_scale",},
{0xffdb, "yn1", "yn1",},
{0xffdc, "yn2", "yn2",},
{0xffdd, "ARAM", "Direct Read from ARAM (uses ADPCM)",},
{0xffde, "GAIN", "Gain",},
{0xffdf, "0xffdf", 0,},
{0xffe0, "0xffe0",0,},
{0xffe1, "0xffe1",0,},
{0xffe2, "0xffe2",0,},
{0xffe3, "0xffe3",0,},
{0xffe4, "0xffe4",0,},
{0xffe5, "0xffe5",0,},
{0xffe6, "0xffe6",0,},
{0xffe7, "0xffe7",0,},
{0xffe8, "0xffe8",0,},
{0xffe9, "0xffe9",0,},
{0xffea, "0xffea",0,},
{0xffeb, "0xffeb",0,},
{0xffec, "0xffec",0,},
{0xffed, "0xffed",0,},
{0xffee, "0xffee",0,},
{0xffef, "AMDM", "ARAM DMA Request Mask",},
{0xfff0, "0xfff0",0,},
{0xfff1, "0xfff1",0,},
{0xfff2, "0xfff2",0,},
{0xfff3, "0xfff3",0,},
{0xfff4, "0xfff4",0,},
{0xfff5, "0xfff5",0,},
{0xfff6, "0xfff6",0,},
{0xfff7, "0xfff7",0,},
{0xfff8, "0xfff8",0,},
{0xfff9, "0xfff9",0,},
{0xfffa, "0xfffa",0,},
{0xfffb, "DIRQ", "DSP IRQ Request",},
{0xfffc, "DMBH", "DSP Mailbox H",},
{0xfffd, "DMBL", "DSP Mailbox L",},
{0xfffe, "CMBH", "CPU Mailbox H",},
{0xffff, "CMBL", "CPU Mailbox L",},
};
const u32 pdlabels_size = sizeof(pdlabels) / sizeof(pdlabel_t);
const pdlabel_t regnames[] =
{
{0x00, "AR0", "Addr Reg 00",},
{0x01, "AR1", "Addr Reg 01",},
{0x02, "AR2", "Addr Reg 02",},
{0x03, "AR3", "Addr Reg 03",},
{0x04, "IX0", "Index Reg 0",},
{0x05, "IX1", "Index Reg 1",},
{0x06, "IX2", "Index Reg 2",},
{0x07, "IX3", "Index Reg 3",},
{0x08, "WR0", "Wrapping Register 0",},
{0x09, "WR1", "Wrapping Register 1",},
{0x0a, "WR2", "Wrapping Register 2",},
{0x0b, "WR3", "Wrapping Register 3",},
{0x0c, "ST0", "Call stack",},
{0x0d, "ST1", "Data stack",},
{0x0e, "ST2", "Loop addr stack",},
{0x0f, "ST3", "Loop counter",},
{0x10, "AC0.H", "Accu High 0",},
{0x11, "AC1.H", "Accu High 1",},
{0x12, "CR", "Config Register",},
{0x13, "SR", "Special Register",},
{0x14, "PROD.L", "Prod L",},
{0x15, "PROD.M1", "Prod M1",},
{0x16, "PROD.H", "Prod H",},
{0x17, "PROD.M2", "Prod M2",},
{0x18, "AX0.L", "Extra Accu L 0",},
{0x19, "AX1.L", "Extra Accu L 1",},
{0x1a, "AX0.H", "Extra Accu H 0",},
{0x1b, "AX1.H", "Extra Accu H 1",},
{0x1c, "AC0.L", "Accu Low 0",},
{0x1d, "AC1.L", "Accu Low 1",},
{0x1e, "AC0.M", "Accu Mid 0",},
{0x1f, "AC1.M", "Accu Mid 1",},
// To resolve combined register names.
{0x20, "ACC0", "Accu Full 0",},
{0x21, "ACC1", "Accu Full 1",},
{0x22, "AX0", "Extra Accu 0",},
{0x23, "AX1", "Extra Accu 1",},
};
const DSPOPCTemplate *opTable[OPTABLE_SIZE];
const DSPOPCTemplate *extOpTable[EXT_OPTABLE_SIZE];
u16 writeBackLog[WRITEBACKLOGSIZE];
int writeBackLogIdx[WRITEBACKLOGSIZE];
const char* pdname(u16 val)
{
static char tmpstr[12]; // nasty
for (int i = 0; i < (int)(sizeof(pdlabels) / sizeof(pdlabel_t)); i++)
{
if (pdlabels[i].addr == val)
return pdlabels[i].name;
}
sprintf(tmpstr, "0x%04x", val);
return tmpstr;
}
const char *pdregname(int val)
{
return regnames[val].name;
}
const char *pdregnamelong(int val)
{
return regnames[val].description;
}
const DSPOPCTemplate *GetOpTemplate(const UDSPInstruction &inst)
{
return opTable[inst];
}
// This function could use the above GetOpTemplate, but then we'd lose the
// nice property that it catches colliding op masks.
void InitInstructionTable()
{
// ext op table
for (int i = 0; i < EXT_OPTABLE_SIZE; i++)
extOpTable[i] = &cw;
for (int i = 0; i < EXT_OPTABLE_SIZE; i++)
{
for (int j = 0; j < opcodes_ext_size; j++)
{
u16 mask = opcodes_ext[j].opcode_mask;
if ((mask & i) == opcodes_ext[j].opcode)
{
if (extOpTable[i] == &cw)
extOpTable[i] = &opcodes_ext[j];
else
ERROR_LOG(DSPLLE, "opcode ext table place %d already in use for %s", i, opcodes_ext[j].name);
}
}
}
// op table
for (int i = 0; i < OPTABLE_SIZE; i++)
opTable[i] = &cw;
for (int i = 0; i < OPTABLE_SIZE; i++)
{
for (int j = 0; j < opcodes_size; j++)
{
u16 mask = opcodes[j].opcode_mask;
if ((mask & i) == opcodes[j].opcode)
{
if (opTable[i] == &cw)
opTable[i] = &opcodes[j];
else
ERROR_LOG(DSPLLE, "opcode table place %d already in use for %s", i, opcodes[j].name);
}
}
}
for (int i=0; i < WRITEBACKLOGSIZE; i++)
writeBackLogIdx[i] = -1;
}

View File

@ -0,0 +1,158 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie (c) 2005 (duddie@walla.com)
#ifndef _DSPTABLES_H
#define _DSPTABLES_H
//nclude "Common.h"
#include "DSPEmitter.h"
#include "DSPCommon.h"
// The non-ADDR ones that end with _D are the opposite one - if the bit specify
// ACC0, then ACC_D will be ACC1.
// The values of these are very important.
// For the reg ones, the value >> 8 is the base register.
// & 0x80 means it's a "D".
enum partype_t
{
P_NONE = 0x0000,
P_VAL = 0x0001,
P_IMM = 0x0002,
P_MEM = 0x0003,
P_STR = 0x0004,
P_ADDR_I = 0x0005,
P_ADDR_D = 0x0006,
P_REG = 0x8000,
P_REG04 = P_REG | 0x0400, // IX
P_REG08 = P_REG | 0x0800,
P_REG18 = P_REG | 0x1800,
P_REGM18 = P_REG | 0x1810, // used in multiply instructions
P_REG19 = P_REG | 0x1900,
P_REGM19 = P_REG | 0x1910, // used in multiply instructions
P_REG1A = P_REG | 0x1a80,
P_REG1C = P_REG | 0x1c00,
// P_ACC = P_REG | 0x1c10, // used for global accum (gcdsptool's value)
P_ACCL = P_REG | 0x1c00, // used for low part of accum
P_ACCM = P_REG | 0x1e00, // used for mid part of accum
// The following are not in gcdsptool
P_ACCM_D = P_REG | 0x1e80,
P_ACC = P_REG | 0x2000, // used for full accum.
P_ACC_D = P_REG | 0x2080,
P_AX = P_REG | 0x2200,
P_REGS_MASK = 0x03f80, // gcdsptool's value = 0x01f80
P_REF = P_REG | 0x4000,
P_PRG = P_REF | P_REG,
// The following seem like junk:
// P_REG10 = P_REG | 0x1000,
// P_AX_D = P_REG | 0x2280,
};
#define OPTABLE_SIZE 0xffff + 1
#define EXT_OPTABLE_SIZE 0xff + 1
typedef void (*dspIntFunc)(const UDSPInstruction);
typedef void (DSPEmitter::*dspJitFunc)(const UDSPInstruction);
struct param2_t
{
partype_t type;
u8 size;
u8 loc;
s8 lshift;
u16 mask;
};
typedef struct
{
const char *name;
u16 opcode;
u16 opcode_mask;
dspIntFunc intFunc;
dspJitFunc jitFunc;
u8 size;
u8 param_count;
param2_t params[8];
bool extended;
bool branch;
bool uncond_branch;
bool reads_pc;
bool updates_sr;
} DSPOPCTemplate;
typedef DSPOPCTemplate opc_t;
// Opcodes
extern const DSPOPCTemplate opcodes[];
extern const int opcodes_size;
extern const DSPOPCTemplate opcodes_ext[];
extern const int opcodes_ext_size;
extern const DSPOPCTemplate cw;
#define WRITEBACKLOGSIZE 5
extern const DSPOPCTemplate *opTable[OPTABLE_SIZE];
extern const DSPOPCTemplate *extOpTable[EXT_OPTABLE_SIZE];
extern u16 writeBackLog[WRITEBACKLOGSIZE];
extern int writeBackLogIdx[WRITEBACKLOGSIZE];
// Predefined labels
struct pdlabel_t
{
u16 addr;
const char* name;
const char* description;
};
extern const pdlabel_t regnames[];
extern const pdlabel_t pdlabels[];
extern const u32 pdlabels_size;
const char *pdname(u16 val);
const char *pdregname(int val);
const char *pdregnamelong(int val);
void InitInstructionTable();
void applyWriteBackLog();
void zeroWriteBackLog();
void zeroWriteBackLogPreserveAcc(u8 acc);
const DSPOPCTemplate *GetOpTemplate(const UDSPInstruction &inst);
inline void ExecuteInstruction(const UDSPInstruction inst)
{
const DSPOPCTemplate *tinst = GetOpTemplate(inst);
if (tinst->extended) {
if ((inst >> 12) == 0x3)
extOpTable[inst & 0x7F]->intFunc(inst);
else
extOpTable[inst & 0xFF]->intFunc(inst);
}
tinst->intFunc(inst);
if (tinst->extended) {
applyWriteBackLog();
}
}
#endif // _DSPTABLES_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,275 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie and Tratax (c) 2004
#include "DSPInterpreter.h"
#include "DSPCore.h"
#include "DSPMemoryMap.h"
#include "DSPStacks.h"
#include "DSPIntCCUtil.h"
#include "DSPIntUtil.h"
namespace DSPInterpreter {
// Generic call implementation
// CALLcc addressA
// 0000 0010 1011 cccc
// aaaa aaaa aaaa aaaa
// Call function if condition cc has been met. Push program counter of
// instruction following "call" to $st0. Set program counter to address
// represented by value that follows this "call" instruction.
void call(const UDSPInstruction opc)
{
// must be outside the if.
u16 dest = dsp_fetch_code();
if (CheckCondition(opc & 0xf))
{
dsp_reg_store_stack(DSP_STACK_C, g_dsp.pc);
g_dsp.pc = dest;
}
}
// Generic callr implementation
// CALLRcc $R
// 0001 0111 rrr1 cccc
// Call function if condition cc has been met. Push program counter of
// instruction following "call" to call stack $st0. Set program counter to
// register $R.
void callr(const UDSPInstruction opc)
{
if (CheckCondition(opc & 0xf))
{
u8 reg = (opc >> 5) & 0x7;
u16 addr = dsp_op_read_reg(reg);
dsp_reg_store_stack(DSP_STACK_C, g_dsp.pc);
g_dsp.pc = addr;
}
}
// Generic if implementation
// IFcc
// 0000 0010 0111 cccc
// Execute following opcode if the condition has been met.
void ifcc(const UDSPInstruction opc)
{
if (!CheckCondition(opc & 0xf))
{
// skip the next opcode - we have to lookup its size.
dsp_skip_inst();
}
}
// Generic jmp implementation
// Jcc addressA
// 0000 0010 1001 cccc
// aaaa aaaa aaaa aaaa
// Jump to addressA if condition cc has been met. Set program counter to
// address represented by value that follows this "jmp" instruction.
void jcc(const UDSPInstruction opc)
{
u16 dest = dsp_fetch_code();
if (CheckCondition(opc & 0xf))
{
g_dsp.pc = dest;
}
}
// Generic jmpr implementation
// JMPcc $R
// 0001 0111 rrr0 cccc
// Jump to address; set program counter to a value from register $R.
void jmprcc(const UDSPInstruction opc)
{
if (CheckCondition(opc & 0xf))
{
u8 reg = (opc >> 5) & 0x7;
g_dsp.pc = dsp_op_read_reg(reg);
}
}
// Generic ret implementation
// RETcc
// 0000 0010 1101 cccc
// Return from subroutine if condition cc has been met. Pops stored PC
// from call stack $st0 and sets $pc to this location.
void ret(const UDSPInstruction opc)
{
if (CheckCondition(opc & 0xf))
{
g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
}
}
// RTI
// 0000 0010 1111 1111
// Return from exception. Pops stored status register $sr from data stack
// $st1 and program counter PC from call stack $st0 and sets $pc to this
// location.
void rti(const UDSPInstruction opc)
{
g_dsp.r.sr = dsp_reg_load_stack(DSP_STACK_D);
g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
}
// HALT
// 0000 0000 0020 0001
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
void halt(const UDSPInstruction opc)
{
g_dsp.cr |= 0x4;
g_dsp.pc--;
}
// LOOP handling: Loop stack is used to control execution of repeated blocks of
// instructions. Whenever there is value on stack $st2 and current PC is equal
// value at $st2, then value at stack $st3 is decremented. If value is not zero
// then PC is modified with value from call stack $st0. Otherwise values from
// call stack $st0 and both loop stacks $st2 and $st3 are popped and execution
// continues at next opcode.
void HandleLoop()
{
// Handle looping hardware.
const u16 rCallAddress = g_dsp.r.st[0];
const u16 rLoopAddress = g_dsp.r.st[2];
u16& rLoopCounter = g_dsp.r.st[3];
if (rLoopAddress > 0 && rLoopCounter > 0)
{
// FIXME: why -1? because we just read past it.
if (g_dsp.pc - 1 == rLoopAddress)
{
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);
}
}
}
}
// LOOP $R
// 0000 0000 010r rrrr
// Repeatedly execute following opcode until counter specified by value
// from register $R reaches zero. Each execution decrement counter. Register
// $R remains unchanged. If register $R is set to zero at the beginning of loop
// then looped instruction will not get executed.
// Actually, this instruction simply prepares the loop stacks for the above.
// The looping hardware takes care of the rest.
void loop(const UDSPInstruction opc)
{
u16 reg = opc & 0x1f;
u16 cnt = dsp_op_read_reg(reg);
u16 loop_pc = g_dsp.pc;
if (cnt)
{
dsp_reg_store_stack(0, g_dsp.pc);
dsp_reg_store_stack(2, loop_pc);
dsp_reg_store_stack(3, cnt);
}
}
// LOOPI #I
// 0001 0000 iiii iiii
// Repeatedly execute following opcode until counter specified by
// immediate value I reaches zero. Each execution decrement counter. If
// immediate value I is set to zero at the beginning of loop then looped
// instruction will not get executed.
// Actually, this instruction simply prepares the loop stacks for the above.
// The looping hardware takes care of the rest.
void loopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
u16 loop_pc = g_dsp.pc;
if (cnt)
{
dsp_reg_store_stack(0, g_dsp.pc);
dsp_reg_store_stack(2, loop_pc);
dsp_reg_store_stack(3, cnt);
}
}
// BLOOP $R, addrA
// 0000 0000 011r rrrr
// aaaa aaaa aaaa aaaa
// Repeatedly execute block of code starting at following opcode until
// counter specified by value from register $R reaches zero. Block ends at
// specified address addrA inclusive, ie. opcode at addrA is the last opcode
// included in loop. Counter is pushed on loop stack $st3, end of block address
// is pushed on loop stack $st2 and repeat address is pushed on call stack $st0.
// Up to 4 nested loops are allowed.
void bloop(const UDSPInstruction opc)
{
u16 reg = opc & 0x1f;
u16 cnt = dsp_op_read_reg(reg);
u16 loop_pc = dsp_fetch_code();
if (cnt)
{
dsp_reg_store_stack(0, g_dsp.pc);
dsp_reg_store_stack(2, loop_pc);
dsp_reg_store_stack(3, cnt);
}
else
{
g_dsp.pc = loop_pc;
dsp_skip_inst();
}
}
// BLOOPI #I, addrA
// 0001 0001 iiii iiii
// aaaa aaaa aaaa aaaa
// Repeatedly execute block of code starting at following opcode until
// counter specified by immediate value I reaches zero. Block ends at specified
// address addrA inclusive, ie. opcode at addrA is the last opcode included in
// loop. Counter is pushed on loop stack $st3, end of block address is pushed
// on loop stack $st2 and repeat address is pushed on call stack $st0. Up to 4
// nested loops are allowed.
void bloopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
u16 loop_pc = dsp_fetch_code();
if (cnt)
{
dsp_reg_store_stack(0, g_dsp.pc);
dsp_reg_store_stack(2, loop_pc);
dsp_reg_store_stack(3, cnt);
}
else
{
g_dsp.pc = loop_pc;
dsp_skip_inst();
}
}
} // namespace

View File

@ -0,0 +1,269 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie and Tratax (c) 2004
#include "DSPInterpreter.h"
#include "DSPMemoryMap.h"
#include "DSPIntUtil.h"
namespace DSPInterpreter {
// SRS @M, $(0x18+S)
// 0010 1sss mmmm mmmm
// Move value from register $(0x18+D) to data memory pointed by address
// CR[0-7] | M. That is, the upper 8 bits of the address are the
// bottom 8 bits from CR, and the lower 8 bits are from the 8-bit immediate.
// Note: pc+=2 in duddie's doc seems wrong
void srs(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
dsp_dmem_write(addr, dsp_op_read_reg(reg));
}
// LRS $(0x18+D), @M
// 0010 0ddd mmmm mmmm
// Move value from data memory pointed by address CR[0-7] | M to register
// $(0x18+D). That is, the upper 8 bits of the address are the bottom 8 bits
// from CR, and the lower 8 bits are from the 8-bit immediate.
void lrs(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
dsp_op_write_reg(reg, dsp_dmem_read(addr));
dsp_conditional_extend_accum(reg);
}
// LR $D, @M
// 0000 0000 110d dddd
// mmmm mmmm mmmm mmmm
// Move value from data memory pointed by address M to register $D.
// FIXME: Perform additional operation depending on destination register.
void lr(const UDSPInstruction opc)
{
u8 reg = opc & DSP_REG_MASK;
u16 addr = dsp_fetch_code();
u16 val = dsp_dmem_read(addr);
dsp_op_write_reg(reg, val);
dsp_conditional_extend_accum(reg);
}
// SR @M, $S
// 0000 0000 111s ssss
// mmmm mmmm mmmm mmmm
// Store value from register $S to a memory pointed by address M.
// FIXME: Perform additional operation depending on destination register.
void sr(const UDSPInstruction opc)
{
u8 reg = opc & DSP_REG_MASK;
u16 addr = dsp_fetch_code();
u16 val = dsp_op_read_reg(reg);
dsp_dmem_write(addr, val);
}
// SI @M, #I
// 0001 0110 mmmm mmmm
// iiii iiii iiii iiii
// Store 16-bit immediate value I to a memory location pointed by address
// M (M is 8-bit value sign extended).
void si(const UDSPInstruction opc)
{
u16 addr = (s8)opc;
u16 imm = dsp_fetch_code();
dsp_dmem_write(addr, imm);
}
// LRR $D, @$S
// 0001 1000 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// FIXME: Perform additional operation depending on destination register.
void lrr(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
}
// LRRD $D, @$S
// 0001 1000 1ssd dddd
// Move value from data memory pointed by addressing register $S toregister $D.
// Decrement register $S.
// FIXME: Perform additional operation depending on destination register.
void lrrd(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[sreg] = dsp_decrement_addr_reg(sreg);
}
// LRRI $D, @$S
// 0001 1001 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Increment register $S.
// FIXME: Perform additional operation depending on destination register.
void lrri(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[sreg] = dsp_increment_addr_reg(sreg);
}
// LRRN $D, @$S
// 0001 1001 1ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Add indexing register $(0x4+S) to register $S.
// FIXME: Perform additional operation depending on destination register.
void lrrn(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
u16 val = dsp_dmem_read(dsp_op_read_reg(sreg));
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[sreg] = dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]);
}
// SRR @$D, $S
// 0001 1010 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D.
// FIXME: Perform additional operation depending on source register.
void srr(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
u16 val = dsp_op_read_reg(sreg);
dsp_dmem_write(g_dsp.r.ar[dreg], val);
}
// SRRD @$D, $S
// 0001 1010 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Decrement register $D.
// FIXME: Perform additional operation depending on source register.
void srrd(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
u16 val = dsp_op_read_reg(sreg);
dsp_dmem_write(g_dsp.r.ar[dreg], val);
g_dsp.r.ar[dreg] = dsp_decrement_addr_reg(dreg);
}
// SRRI @$D, $S
// 0001 1011 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Increment register $D.
// FIXME: Perform additional operation depending on source register.
void srri(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
u16 val = dsp_op_read_reg(sreg);
dsp_dmem_write(g_dsp.r.ar[dreg], val);
g_dsp.r.ar[dreg] = dsp_increment_addr_reg(dreg);
}
// SRRN @$D, $S
// 0001 1011 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Add DSP_REG_IX0 register to register $D.
// FIXME: Perform additional operation depending on source register.
void srrn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
u16 val = dsp_op_read_reg(sreg);
dsp_dmem_write(g_dsp.r.ar[dreg], val);
g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]);
}
// ILRR $acD.m, @$arS
// 0000 001d 0001 00ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m.
void ilrr(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
g_dsp.r.ac[dreg-DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
}
// ILRRD $acD.m, @$arS
// 0000 001d 0001 01ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Decrement addressing register $arS.
void ilrrd(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
g_dsp.r.ac[dreg-DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[reg] = dsp_decrement_addr_reg(reg);
}
// ILRRI $acD.m, @$S
// 0000 001d 0001 10ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Increment addressing register $arS.
void ilrri(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
g_dsp.r.ac[dreg-DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[reg] = dsp_increment_addr_reg(reg);
}
// ILRRN $acD.m, @$arS
// 0000 001d 0001 11ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Add corresponding indexing
// register $ixS to addressing register $arS.
void ilrrn(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = DSP_REG_ACM0 + ((opc >> 8) & 1);
g_dsp.r.ac[dreg-DSP_REG_ACM0].m = dsp_imem_read(g_dsp.r.ar[reg]);
dsp_conditional_extend_accum(dreg);
g_dsp.r.ar[reg] = dsp_increase_addr_reg(reg, (s16)g_dsp.r.ix[reg]);
}
} // namespace

View File

@ -0,0 +1,189 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie and Tratax (c) 2004
#include "DSPInterpreter.h"
#include "DSPCore.h"
#include "DSPIntUtil.h"
namespace DSPInterpreter {
// MRR $D, $S
// 0001 11dd ddds ssss
// Move value from register $S to register $D.
// todo: Perform additional operation depending on destination register.
void mrr(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1f;
u8 dreg = (opc >> 5) & 0x1f;
u16 val = dsp_op_read_reg(sreg);
dsp_op_write_reg(dreg, val);
dsp_conditional_extend_accum(dreg);
}
// LRI $D, #I
// 0000 0000 100d dddd
// iiii iiii iiii iiii
// Load immediate value I to register $D.
//
// DSPSpy discovery: This, and possibly other instructions that load a
// register, has a different behaviour in S40 mode if loaded to AC0.M: The
// value gets sign extended to the whole accumulator! This does not happen in
// S16 mode.
void lri(const UDSPInstruction opc)
{
u8 reg = opc & DSP_REG_MASK;
u16 imm = dsp_fetch_code();
dsp_op_write_reg(reg, imm);
dsp_conditional_extend_accum(reg);
}
// LRIS $(0x18+D), #I
// 0000 1ddd iiii iiii
// Load immediate value I (8-bit sign extended) to accumulator register.
void lris(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
u16 imm = (s8)opc;
dsp_op_write_reg(reg, imm);
dsp_conditional_extend_accum(reg);
}
//----
// NX
// 1000 -000 xxxx xxxx
// No operation, but can be extended with extended opcode.
// This opcode is supposed to do nothing - it's used if you want to use
// an opcode extension but not do anything. At least according to duddie.
void nx(const UDSPInstruction opc)
{
zeroWriteBackLog();
}
//----
// DAR $arD
// 0000 0000 0000 01dd
// Decrement address register $arD.
void dar(const UDSPInstruction opc)
{
g_dsp.r.ar[opc & 0x3] = dsp_decrement_addr_reg(opc & 0x3);
}
// IAR $arD
// 0000 0000 0000 10dd
// Increment address register $arD.
void iar(const UDSPInstruction opc)
{
g_dsp.r.ar[opc & 0x3] = dsp_increment_addr_reg(opc & 0x3);
}
// SUBARN $arD
// 0000 0000 0000 11dd
// Subtract indexing register $ixD from an addressing register $arD.
// used only in IPL-NTSC ucode
void subarn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
g_dsp.r.ar[dreg] = dsp_decrease_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]);
}
// ADDARN $arD, $ixS
// 0000 0000 0001 ssdd
// Adds indexing register $ixS to an addressing register $arD.
// It is critical for the Zelda ucode that this one wraps correctly.
void addarn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = (opc >> 2) & 0x3;
g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[sreg]);
}
//----
// SBCLR #I
// 0001 0011 aaaa aiii
// bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void sbclr(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
g_dsp.r.sr &= ~(1 << bit);
}
// SBSET #I
// 0001 0010 aaaa aiii
// Set bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void sbset(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
g_dsp.r.sr |= (1 << bit);
}
// This is a bunch of flag setters, flipping bits in SR. So far so good,
// but it's harder to know exactly what effect they have.
void srbith(const UDSPInstruction opc)
{
zeroWriteBackLog();
switch ((opc >> 8) & 0xf)
{
// M0/M2 change the multiplier mode (it can multiply by 2 for free).
case 0xa: // M2
g_dsp.r.sr &= ~SR_MUL_MODIFY;
break;
case 0xb: // M0
g_dsp.r.sr |= SR_MUL_MODIFY;
break;
// If set, treat multiplicands as unsigned.
// If clear, treat them as signed.
case 0xc: // CLR15
g_dsp.r.sr &= ~SR_MUL_UNSIGNED;
break;
case 0xd: // SET15
g_dsp.r.sr |= SR_MUL_UNSIGNED;
break;
// Automatic 40-bit sign extension when loading ACx.M.
// SET40 changes something very important: see the LRI instruction above.
case 0xe: // SET16 (CLR40)
g_dsp.r.sr &= ~SR_40_MODE_BIT;
break;
case 0xf: // SET40
g_dsp.r.sr |= SR_40_MODE_BIT;
break;
default:
break;
}
}
//----
void unknown(const UDSPInstruction opc)
{
ERROR_LOG(DSPLLE, "LLE: Unrecognized opcode 0x%04x, pc 0x%04x", opc, g_dsp.pc);
}
} // namespace

View File

@ -0,0 +1,609 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie and Tratax (c) 2004
// Multiplier and product register control
#include "DSPInterpreter.h"
#include "DSPIntCCUtil.h"
#include "DSPIntUtil.h"
namespace DSPInterpreter {
// Only MULX family instructions have unsigned/mixed support.
inline s64 dsp_get_multiply_prod(u16 a, u16 b, u8 sign)
{
s64 prod;
if ((sign == 1) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) //unsigned
prod = (u32)(a * b);
else if ((sign == 2) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) //mixed
prod = a * (s16)b;
else
prod = (s16)a * (s16)b; //signed
// Conditionally multiply by 2.
if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
prod <<= 1;
return prod;
}
inline s64 dsp_multiply(u16 a, u16 b, u8 sign = 0)
{
s64 prod = dsp_get_multiply_prod(a, b, sign);
return prod;
}
inline s64 dsp_multiply_add(u16 a, u16 b, u8 sign = 0)
{
s64 prod = dsp_get_long_prod() + dsp_get_multiply_prod(a, b, sign);
return prod;
}
inline s64 dsp_multiply_sub(u16 a, u16 b, u8 sign = 0)
{
s64 prod = dsp_get_long_prod() - dsp_get_multiply_prod(a, b, sign);
return prod;
}
inline s64 dsp_multiply_mulx(u8 axh0, u8 axh1, u16 val1, u16 val2)
{
s64 result;
if ((axh0==0) && (axh1==0))
result = dsp_multiply(val1, val2, 1); // unsigned support ON if both ax?.l regs are used
else if ((axh0==0) && (axh1==1))
result = dsp_multiply(val1, val2, 2); // mixed support ON (u16)axl.0 * (s16)axh.1
else if ((axh0==1) && (axh1==0))
result = dsp_multiply(val2, val1, 2); // mixed support ON (u16)axl.1 * (s16)axh.0
else
result = dsp_multiply(val1, val2, 0); // unsigned support OFF if both ax?.h regs are used
return result;
}
//----
// CLRP
// 1000 0100 xxxx xxxx
// Clears product register $prod.
// Magic numbers taken from duddie's doc
//
// 00ff_(fff0 + 0010)_0000 = 0100_0000_0000, conveniently, lower 40bits = 0
//
// It's not ok, to just zero all of them, correct values should be set because of
// direct use of prod regs by AX/AXWII (look @that part of ucode).
void clrp(const UDSPInstruction opc)
{
zeroWriteBackLog();
g_dsp.r.prod.l = 0x0000;
g_dsp.r.prod.m = 0xfff0;
g_dsp.r.prod.h = 0x00ff;
g_dsp.r.prod.m2 = 0x0010;
}
// TSTPROD
// 1000 0101 xxxx xxxx
// Test prod regs value.
//
// flags out: --xx xx0x
void tstprod(const UDSPInstruction opc)
{
s64 prod = dsp_get_long_prod();
Update_SR_Register64(prod);
zeroWriteBackLog();
}
//----
// MOVP $acD
// 0110 111d xxxx xxxx
// Moves multiply product from $prod register to accumulator $acD register.
//
// flags out: --xx xx0x
void movp(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x1;
s64 acc = dsp_get_long_prod();
zeroWriteBackLog();
dsp_set_long_acc(dreg, acc);
Update_SR_Register64(acc);
}
// MOVNP $acD
// 0111 111d xxxx xxxx
// Moves negative of multiply product from $prod register to accumulator
// $acD register.
//
// flags out: --xx xx0x
void movnp(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x1;
s64 acc = -dsp_get_long_prod();
zeroWriteBackLog();
dsp_set_long_acc(dreg, acc);
Update_SR_Register64(acc);
}
// MOVPZ $acD
// 1111 111d xxxx xxxx
// Moves multiply product from $prod register to accumulator $acD
// register and sets (rounds) $acD.l to 0
//
// flags out: --xx xx0x
void movpz(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x01;
s64 acc = dsp_get_long_prod_round_prodl();
zeroWriteBackLog();
dsp_set_long_acc(dreg, acc);
Update_SR_Register64(acc);
}
// ADDPAXZ $acD, $axS
// 1111 10sd xxxx xxxx
// Adds secondary accumulator $axS to product register and stores result
// in accumulator register. Low 16-bits of $acD ($acD.l) are set (round) to 0.
//
// TODO: ugly code and still small error here (+/- 1 in .m - randomly)
// flags out: --xx xx0x
void addpaxz(const UDSPInstruction opc)
{
u8 dreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
s64 oldprod = dsp_get_long_prod();
s64 prod = dsp_get_long_prod_round_prodl();
s64 ax = dsp_get_long_acx(sreg);
s64 res = prod + (ax & ~0xffff);
zeroWriteBackLog();
dsp_set_long_acc(dreg, res);
res = dsp_get_long_acc(dreg);
Update_SR_Register64(res, isCarry(oldprod, res), false);
}
//----
// MULAXH
// 1000 0011 xxxx xxxx
// Multiply $ax0.h by $ax0.h
void mulaxh(const UDSPInstruction opc)
{
s64 prod = dsp_multiply(dsp_get_ax_h(0), dsp_get_ax_h(0));
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
//----
// MUL $axS.l, $axS.h
// 1001 s000 xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed).
void mul(const UDSPInstruction opc)
{
u8 sreg = (opc >> 11) & 0x1;
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axh, axl);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MULAC $axS.l, $axS.h, $acR
// 1001 s10r xxxx xxxx
// Add product register to accumulator register $acR. Multiply low part
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
// accumulator $axS (treat them both as signed).
//
// flags out: --xx xx0x
void mulac(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 11) & 0x1;
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULMV $axS.l, $axS.h, $acR
// 1001 s11r xxxx xxxx
// Move product register to accumulator register $acR. Multiply low part
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
// accumulator $axS (treat them both as signed).
//
// flags out: --xx xx0x
void mulmv(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = ((opc >> 11) & 0x1);
s64 acc = dsp_get_long_prod();
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULMVZ $axS.l, $axS.h, $acR
// 1001 s01r xxxx xxxx
// Move product register to accumulator register $acR and clear (round) low part
// of accumulator register $acR.l. Multiply low part $axS.l of secondary
// accumulator $axS by high part $axS.h of secondary accumulator $axS (treat
// them both as signed).
//
// flags out: --xx xx0x
void mulmvz(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 11) & 0x1;
s64 acc = dsp_get_long_prod_round_prodl();
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
//----
// MULX $ax0.S, $ax1.T
// 101s t000 xxxx xxxx
// Multiply one part $ax0 by one part $ax1.
// Part is selected by S and T bits. Zero selects low part, one selects high part.
void mulx(const UDSPInstruction opc)
{
u8 treg = ((opc >> 11) & 0x1);
u8 sreg = ((opc >> 12) & 0x1);
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MULXAC $ax0.S, $ax1.T, $acR
// 101s t01r xxxx xxxx
// Add product register to accumulator register $acR. Multiply one part
// $ax0 by one part $ax1. Part is selected by S and
// T bits. Zero selects low part, one selects high part.
//
// flags out: --xx xx0x
void mulxac(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULXMV $ax0.S, $ax1.T, $acR
// 101s t11r xxxx xxxx
// Move product register to accumulator register $acR. Multiply one part
// $ax0 by one part $ax1. Part is selected by S and
// T bits. Zero selects low part, one selects high part.
//
// flags out: --xx xx0x
void mulxmv(const UDSPInstruction opc)
{
u8 rreg = ((opc >> 8) & 0x1);
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod();
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULXMVZ $ax0.S, $ax1.T, $acR
// 101s t01r xxxx xxxx
// Move product register to accumulator register $acR and clear (round) low part
// of accumulator register $acR.l. Multiply one part $ax0 by one part $ax1
// Part is selected by S and T bits. Zero selects low part,
// one selects high part.
//
// flags out: --xx xx0x
void mulxmvz(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod_round_prodl();
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
//----
// MULC $acS.m, $axT.h
// 110s t000 xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
// secondary accumulator $axS (treat them both as signed).
void mulc(const UDSPInstruction opc)
{
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MULCAC $acS.m, $axT.h, $acR
// 110s t10r xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
// secondary accumulator $axS (treat them both as signed). Add product
// register before multiplication to accumulator $acR.
//
// flags out: --xx xx0x
void mulcac(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULCMV $acS.m, $axT.h, $acR
// 110s t11r xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
// secondary accumulator $axT (treat them both as signed). Move product
// register before multiplication to accumulator $acR.
// possible mistake in duddie's doc axT.h rather than axS.h
//
// flags out: --xx xx0x
void mulcmv(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod();
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
// MULCMVZ $acS.m, $axT.h, $acR
// 110s t01r xxxx xxxx
// (fixed possible bug in duddie's description, s->t)
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
// secondary accumulator $axT (treat them both as signed). Move product
// register before multiplication to accumulator $acR, set (round) low part of
// accumulator $acR.l to zero.
//
// flags out: --xx xx0x
void mulcmvz(const UDSPInstruction opc)
{
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
s64 acc = dsp_get_long_prod_round_prodl();
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
dsp_set_long_acc(rreg, acc);
Update_SR_Register64(dsp_get_long_acc(rreg));
}
//----
// MADDX ax0.S ax1.T
// 1110 00st xxxx xxxx
// Multiply one part of secondary accumulator $ax0 (selected by S) by
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
// signed) and add result to product register.
void maddx(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_add(val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MSUBX $(0x18+S*2), $(0x19+T*2)
// 1110 01st xxxx xxxx
// Multiply one part of secondary accumulator $ax0 (selected by S) by
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
// signed) and subtract result from product register.
void msubx(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
s64 prod = dsp_multiply_sub(val1, val2);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MADDC $acS.m, $axT.h
// 1110 10st xxxx xxxx
// Multiply middle part of accumulator $acS.m by high part of secondary
// accumulator $axT.h (treat them both as signed) and add result to product
// register.
void maddc(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply_add(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MSUBC $acS.m, $axT.h
// 1110 11st xxxx xxxx
// Multiply middle part of accumulator $acS.m by high part of secondary
// accumulator $axT.h (treat them both as signed) and subtract result from
// product register.
void msubc(const UDSPInstruction opc)
{
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
u16 accm = dsp_get_acc_m(sreg);
u16 axh = dsp_get_ax_h(treg);
s64 prod = dsp_multiply_sub(accm, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MADD $axS.l, $axS.h
// 1111 001s xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed) and add
// result to product register.
void madd(const UDSPInstruction opc)
{
u8 sreg = (opc >> 8) & 0x1;
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply_add(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
// MSUB $axS.l, $axS.h
// 1111 011s xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed) and
// subtract result from product register.
void msub(const UDSPInstruction opc)
{
u8 sreg = (opc >> 8) & 0x1;
u16 axl = dsp_get_ax_l(sreg);
u16 axh = dsp_get_ax_h(sreg);
s64 prod = dsp_multiply_sub(axl, axh);
zeroWriteBackLog();
dsp_set_long_prod(prod);
}
} // namespace

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,606 @@
// Copyright (C) 2010 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "../DSPMemoryMap.h"
#include "../DSPEmitter.h"
#include "../DSPStacks.h"
#include "../DSPAnalyzer.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
using namespace Gen;
template <void(*jitCode)(const UDSPInstruction, DSPEmitter&)>
static void ReJitConditional(const UDSPInstruction opc, DSPEmitter& emitter)
{
u8 cond = opc & 0xf;
if (cond == 0xf) {// Always true.
jitCode(opc,emitter);
return;
}
FixupBranch skipCode2;
emitter.dsp_op_read_reg(DSP_REG_SR, RAX);
DSPJitRegCache c2(emitter.gpr);
switch(cond)
{
case 0x0: // GE - Greater Equal
case 0x1: // L - Less
case 0x2: // G - Greater
case 0x3: // LE - Less Equal
emitter.MOV(16, R(EDX), R(EAX));
emitter.SHR(16, R(EDX), Imm8(3)); //SR_SIGN flag
emitter.NOT(16, R(EDX));
emitter.SHR(16, R(EAX), Imm8(1)); //SR_OVERFLOW flag
emitter.NOT(16, R(EAX));
emitter.XOR(16, R(EAX), R(EDX));
if (cond < 0x2) {
emitter.TEST(16, R(EAX), Imm16(1));
break;
}
c2 = emitter.gpr;
emitter.TEST(16, R(EAX), Imm16(1));
//LE: problem in here, half the tests fail
skipCode2 = emitter.J_CC(CC_NE);
//skipCode2 = emitter.J_CC((CCFlags)(CC_NE - (cond & 1)));
emitter.dsp_op_read_reg(DSP_REG_SR, RAX);
emitter.TEST(16, R(EAX), Imm16(SR_ARITH_ZERO));
break;
case 0x4: // NZ - Not Zero
case 0x5: // Z - Zero
emitter.TEST(16, R(EAX), Imm16(SR_ARITH_ZERO));
break;
case 0x6: // NC - Not carry
case 0x7: // C - Carry
emitter.TEST(16, R(EAX), Imm16(SR_CARRY));
break;
case 0x8: // ? - Not over s32
case 0x9: // ? - Over s32
emitter.TEST(16, R(EAX), Imm16(SR_OVER_S32));
break;
case 0xa: // ?
case 0xb: // ?
{
//full of fail, both
emitter.TEST(16, R(EAX), Imm16(SR_OVER_S32 | SR_TOP2BITS));
FixupBranch skipArithZero = emitter.J_CC(CC_E);
emitter.TEST(16, R(EAX), Imm16(SR_ARITH_ZERO));
FixupBranch setZero = emitter.J_CC(CC_NE);
emitter.MOV(16, R(EAX), Imm16(1));
FixupBranch toEnd = emitter.J();
emitter.SetJumpTarget(skipArithZero);
emitter.SetJumpTarget(setZero);
emitter.XOR(16, R(EAX), R(EAX));
emitter.SetJumpTarget(toEnd);
emitter.SETcc(CC_E, R(EAX));
emitter.TEST(8, R(EAX), R(EAX));
break;
//c2 = emitter.gpr;
//emitter.TEST(16, R(EAX), Imm16(SR_OVER_S32 | SR_TOP2BITS));
//skipCode2 = emitter.J_CC((CCFlags)(CC_E + (cond & 1)));
//emitter.TEST(16, R(EAX), Imm16(SR_ARITH_ZERO));
//break;
}
case 0xc: // LNZ - Logic Not Zero
case 0xd: // LZ - Logic Zero
emitter.TEST(16, R(EAX), Imm16(SR_LOGIC_ZERO));
break;
case 0xe: // 0 - Overflow
emitter.TEST(16, R(EAX), Imm16(SR_OVERFLOW));
break;
}
DSPJitRegCache c1(emitter.gpr);
FixupBranch skipCode = cond == 0xe ? emitter.J_CC(CC_E) : emitter.J_CC((CCFlags)(CC_NE - (cond & 1)));
jitCode(opc,emitter);
emitter.gpr.flushRegs(c1);
emitter.SetJumpTarget(skipCode);
if ((cond | 1) == 0x3) {// || (cond | 1) == 0xb)
emitter.gpr.flushRegs(c2);
emitter.SetJumpTarget(skipCode2);
} else {
c2.drop();
}
}
static void WriteBranchExit(DSPEmitter& emitter)
{
emitter.SaveDSPRegs();
if (DSPAnalyzer::code_flags[emitter.startAddr] & DSPAnalyzer::CODE_IDLE_SKIP)
{
emitter.MOV(16, R(EAX), Imm16(0x1000));
}
else
{
emitter.MOV(16, R(EAX), Imm16(emitter.blockSize[emitter.startAddr]));
}
emitter.JMP(emitter.returnDispatcher, true);
}
static void WriteBlockLink(DSPEmitter& emitter, u16 dest)
{
// Jump directly to the called block if it has already been compiled.
if (!(dest >= emitter.startAddr && dest <= emitter.compilePC))
{
if (emitter.blockLinks[dest] != 0 )
{
emitter.gpr.flushRegs();
#ifdef _M_IX86 // All32
// Check if we have enough cycles to execute the next block
emitter.MOV(16, R(ESI), M(&cyclesLeft));
emitter.CMP(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
emitter.SUB(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr]));
emitter.MOV(16, M(&cyclesLeft), R(ESI));
#else
// Check if we have enough cycles to execute the next block
emitter.MOV(64, R(R12), ImmPtr(&cyclesLeft));
emitter.CMP(16, MatR(R12), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
emitter.SUB(16, MatR(R12), Imm16(emitter.blockSize[emitter.startAddr]));
#endif
emitter.JMP(emitter.blockLinks[dest], true);
emitter.SetJumpTarget(notEnoughCycles);
}
else
{
// The destination has not been compiled yet. Add it to the list
// of blocks that this block is waiting on.
emitter.unresolvedJumps[emitter.startAddr].push_back(dest);
}
}
}
void r_jcc(const UDSPInstruction opc, DSPEmitter& emitter)
{
u16 dest = dsp_imem_read(emitter.compilePC + 1);
const DSPOPCTemplate *opcode = GetOpTemplate(opc);
// If the block is unconditional, attempt to link block
if (opcode->uncond_branch)
WriteBlockLink(emitter, dest);
#ifdef _M_IX86 // All32
emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest));
#else
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), Imm16(dest));
#endif
WriteBranchExit(emitter);
}
// Generic jmp implementation
// Jcc addressA
// 0000 0010 1001 cccc
// aaaa aaaa aaaa aaaa
// Jump to addressA if condition cc has been met. Set program counter to
// address represented by value that follows this "jmp" instruction.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::jcc(const UDSPInstruction opc)
{
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 2));
#endif
ReJitConditional<r_jcc>(opc, *this);
}
void r_jmprcc(const UDSPInstruction opc, DSPEmitter& emitter)
{
u8 reg = (opc >> 5) & 0x7;
//reg can only be DSP_REG_ARx and DSP_REG_IXx now,
//no need to handle DSP_REG_STx.
emitter.dsp_op_read_reg(reg, RAX, NONE);
#ifdef _M_IX86 // All32
emitter.MOV(16, M(&g_dsp.pc), R(EAX));
#else
emitter.MOV(64, R(RSI), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RSI), R(RAX));
#endif
WriteBranchExit(emitter);
}
// Generic jmpr implementation
// JMPcc $R
// 0001 0111 rrr0 cccc
// Jump to address; set program counter to a value from register $R.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::jmprcc(const UDSPInstruction opc)
{
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.pc), Imm16(compilePC + 1));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 1));
#endif
ReJitConditional<r_jmprcc>(opc, *this);
}
void r_call(const UDSPInstruction opc, DSPEmitter& emitter)
{
emitter.MOV(16, R(DX), Imm16(emitter.compilePC + 2));
emitter.dsp_reg_store_stack(DSP_STACK_C);
u16 dest = dsp_imem_read(emitter.compilePC + 1);
const DSPOPCTemplate *opcode = GetOpTemplate(opc);
// If the block is unconditional, attempt to link block
if (opcode->uncond_branch)
WriteBlockLink(emitter, dest);
#ifdef _M_IX86 // All32
emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest));
#else
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), Imm16(dest));
#endif
WriteBranchExit(emitter);
}
// Generic call implementation
// CALLcc addressA
// 0000 0010 1011 cccc
// aaaa aaaa aaaa aaaa
// Call function if condition cc has been met. Push program counter of
// instruction following "call" to $st0. Set program counter to address
// represented by value that follows this "call" instruction.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::call(const UDSPInstruction opc)
{
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 2));
#endif
ReJitConditional<r_call>(opc, *this);
}
void r_callr(const UDSPInstruction opc, DSPEmitter& emitter)
{
u8 reg = (opc >> 5) & 0x7;
emitter.MOV(16, R(DX), Imm16(emitter.compilePC + 1));
emitter.dsp_reg_store_stack(DSP_STACK_C);
emitter.dsp_op_read_reg(reg, RAX, NONE);
#ifdef _M_IX86 // All32
emitter.MOV(16, M(&g_dsp.pc), R(EAX));
#else
emitter.MOV(64, R(RSI), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RSI), R(RAX));
#endif
WriteBranchExit(emitter);
}
// Generic callr implementation
// CALLRcc $R
// 0001 0111 rrr1 cccc
// Call function if condition cc has been met. Push program counter of
// instruction following "call" to call stack $st0. Set program counter to
// register $R.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::callr(const UDSPInstruction opc)
{
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.pc), Imm16(compilePC + 1));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 1));
#endif
ReJitConditional<r_callr>(opc, *this);
}
void r_ifcc(const UDSPInstruction opc, DSPEmitter& emitter)
{
#ifdef _M_IX86 // All32
emitter.MOV(16, M(&g_dsp.pc), Imm16(emitter.compilePC + 1));
#else
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), Imm16(emitter.compilePC + 1));
#endif
}
// Generic if implementation
// IFcc
// 0000 0010 0111 cccc
// Execute following opcode if the condition has been met.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::ifcc(const UDSPInstruction opc)
{
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.pc), Imm16((compilePC + 1) + opTable[compilePC + 1]->size));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16((compilePC + 1) + opTable[compilePC + 1]->size));
#endif
ReJitConditional<r_ifcc>(opc, *this);
WriteBranchExit(*this);
}
void r_ret(const UDSPInstruction opc, DSPEmitter& emitter)
{
emitter.dsp_reg_load_stack(DSP_STACK_C);
#ifdef _M_IX86 // All32
emitter.MOV(16, M(&g_dsp.pc), R(DX));
#else
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), R(DX));
#endif
WriteBranchExit(emitter);
}
// Generic ret implementation
// RETcc
// 0000 0010 1101 cccc
// Return from subroutine if condition cc has been met. Pops stored PC
// from call stack $st0 and sets $pc to this location.
// NOTE: Cannot use Default(opc) here because of the need to write branch exit
void DSPEmitter::ret(const UDSPInstruction opc)
{
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.pc), Imm16(compilePC + 1));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 1));
#endif
ReJitConditional<r_ret>(opc, *this);
}
// RTI
// 0000 0010 1111 1111
// Return from exception. Pops stored status register $sr from data stack
// $st1 and program counter PC from call stack $st0 and sets $pc to this
// location.
void DSPEmitter::rti(const UDSPInstruction opc)
{
// g_dsp.r[DSP_REG_SR] = dsp_reg_load_stack(DSP_STACK_D);
dsp_reg_load_stack(DSP_STACK_D);
dsp_op_write_reg(DSP_REG_SR, RDX);
// g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
dsp_reg_load_stack(DSP_STACK_C);
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.pc), R(DX));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), R(DX));
#endif
}
// HALT
// 0000 0000 0020 0001
// Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR.
void DSPEmitter::halt(const UDSPInstruction opc)
{
#ifdef _M_IX86 // All32
OR(16, M(&g_dsp.cr), Imm16(4));
#else
MOV(64, R(RAX), ImmPtr(&g_dsp.cr));
OR(16, MatR(RAX), Imm16(4));
#endif
// g_dsp.pc = dsp_reg_load_stack(DSP_STACK_C);
dsp_reg_load_stack(DSP_STACK_C);
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.pc), R(DX));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), R(DX));
#endif
}
// LOOP handling: Loop stack is used to control execution of repeated blocks of
// instructions. Whenever there is value on stack $st2 and current PC is equal
// value at $st2, then value at stack $st3 is decremented. If value is not zero
// then PC is modified with value from call stack $st0. Otherwise values from
// call stack $st0 and both loop stacks $st2 and $st3 are popped and execution
// continues at next opcode.
void DSPEmitter::HandleLoop()
{
#ifdef _M_IX86 // All32
MOVZX(32, 16, EAX, M(&g_dsp.r.st[2]));
MOVZX(32, 16, ECX, M(&g_dsp.r.st[3]));
#else
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
MOVZX(32, 16, EAX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[2])));
MOVZX(32, 16, ECX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[3])));
#endif
CMP(32, R(RCX), Imm32(0));
FixupBranch rLoopCntG = J_CC(CC_LE, true);
CMP(16, R(RAX), Imm16(compilePC - 1));
FixupBranch rLoopAddrG = J_CC(CC_NE, true);
#ifdef _M_IX86 // All32
SUB(16, M(&(g_dsp.r.st[3])), Imm16(1));
CMP(16, M(&(g_dsp.r.st[3])), Imm16(0));
#else
SUB(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[3])), Imm16(1));
CMP(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[3])), Imm16(0));
#endif
FixupBranch loadStack = J_CC(CC_LE, true);
#ifdef _M_IX86 // All32
MOVZX(32, 16, ECX, M(&(g_dsp.r.st[0])));
MOV(16, M(&g_dsp.pc), R(RCX));
#else
MOVZX(32, 16, RCX, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[0])));
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), R(RCX));
#endif
FixupBranch loopUpdated = J(true);
SetJumpTarget(loadStack);
dsp_reg_load_stack(0);
dsp_reg_load_stack(2);
dsp_reg_load_stack(3);
SetJumpTarget(loopUpdated);
SetJumpTarget(rLoopAddrG);
SetJumpTarget(rLoopCntG);
}
// LOOP $R
// 0000 0000 010r rrrr
// Repeatedly execute following opcode until counter specified by value
// from register $R reaches zero. Each execution decrement counter. Register
// $R remains unchanged. If register $R is set to zero at the beginning of loop
// then looped instruction will not get executed.
// Actually, this instruction simply prepares the loop stacks for the above.
// The looping hardware takes care of the rest.
void DSPEmitter::loop(const UDSPInstruction opc)
{
u16 reg = opc & 0x1f;
// u16 cnt = g_dsp.r[reg];
dsp_op_read_reg(reg, RDX, ZERO);
u16 loop_pc = compilePC + 1;
CMP(16, R(EDX), Imm16(0));
FixupBranch cnt = J_CC(CC_Z, true);
dsp_reg_store_stack(3);
MOV(16, R(RDX), Imm16(compilePC + 1));
dsp_reg_store_stack(0);
MOV(16, R(RDX), Imm16(loop_pc));
dsp_reg_store_stack(2);
SetJumpTarget(cnt);
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 1));
#endif
}
// LOOPI #I
// 0001 0000 iiii iiii
// Repeatedly execute following opcode until counter specified by
// immediate value I reaches zero. Each execution decrement counter. If
// immediate value I is set to zero at the beginning of loop then looped
// instruction will not get executed.
// Actually, this instruction simply prepares the loop stacks for the above.
// The looping hardware takes care of the rest.
void DSPEmitter::loopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
u16 loop_pc = compilePC + 1;
if (cnt)
{
MOV(16, R(RDX), Imm16(compilePC + 1));
dsp_reg_store_stack(0);
MOV(16, R(RDX), Imm16(loop_pc));
dsp_reg_store_stack(2);
MOV(16, R(RDX), Imm16(cnt));
dsp_reg_store_stack(3);
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 1));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 1));
#endif
}
}
// BLOOP $R, addrA
// 0000 0000 011r rrrr
// aaaa aaaa aaaa aaaa
// Repeatedly execute block of code starting at following opcode until
// counter specified by value from register $R reaches zero. Block ends at
// specified address addrA inclusive, ie. opcode at addrA is the last opcode
// included in loop. Counter is pushed on loop stack $st3, end of block address
// is pushed on loop stack $st2 and repeat address is pushed on call stack $st0.
// Up to 4 nested loops are allowed.
void DSPEmitter::bloop(const UDSPInstruction opc)
{
u16 reg = opc & 0x1f;
// u16 cnt = g_dsp.r[reg];
dsp_op_read_reg(reg, RDX, ZERO);
u16 loop_pc = dsp_imem_read(compilePC + 1);
CMP(16, R(EDX), Imm16(0));
FixupBranch cnt = J_CC(CC_Z, true);
dsp_reg_store_stack(3);
MOV(16, R(RDX), Imm16(compilePC + 2));
dsp_reg_store_stack(0);
MOV(16, R(RDX), Imm16(loop_pc));
dsp_reg_store_stack(2);
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 2));
#endif
FixupBranch exit = J();
SetJumpTarget(cnt);
// g_dsp.pc = loop_pc;
// dsp_skip_inst();
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.pc), Imm16(loop_pc + opTable[loop_pc]->size));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(loop_pc + opTable[loop_pc]->size));
#endif
DSPJitRegCache c(gpr);
WriteBranchExit(*this);
gpr.flushRegs(c,false);
SetJumpTarget(exit);
}
// BLOOPI #I, addrA
// 0001 0001 iiii iiii
// aaaa aaaa aaaa aaaa
// Repeatedly execute block of code starting at following opcode until
// counter specified by immediate value I reaches zero. Block ends at specified
// address addrA inclusive, ie. opcode at addrA is the last opcode included in
// loop. Counter is pushed on loop stack $st3, end of block address is pushed
// on loop stack $st2 and repeat address is pushed on call stack $st0. Up to 4
// nested loops are allowed.
void DSPEmitter::bloopi(const UDSPInstruction opc)
{
u16 cnt = opc & 0xff;
// u16 loop_pc = dsp_fetch_code();
u16 loop_pc = dsp_imem_read(compilePC + 1);
if (cnt)
{
MOV(16, R(RDX), Imm16(compilePC + 2));
dsp_reg_store_stack(0);
MOV(16, R(RDX), Imm16(loop_pc));
dsp_reg_store_stack(2);
MOV(16, R(RDX), Imm16(cnt));
dsp_reg_store_stack(3);
#ifdef _M_IX86 // All32
MOV(16, M(&(g_dsp.pc)), Imm16(compilePC + 2));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(compilePC + 2));
#endif
}
else
{
// g_dsp.pc = loop_pc;
// dsp_skip_inst();
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.pc), Imm16(loop_pc + opTable[loop_pc]->size));
#else
MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
MOV(16, MatR(RAX), Imm16(loop_pc + opTable[loop_pc]->size));
#endif
WriteBranchExit(*this);
}
}

View File

@ -0,0 +1,323 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie and Tratax (c) 2004
// HELPER FUNCTIONS
#include "../DSPIntUtil.h"
#include "../DSPEmitter.h"
#include "x64Emitter.h"
#include "ABI.h"
using namespace Gen;
// In: RAX: s64 _Value
// In: RCX: 1 = carry, 2 = overflow
// Clobbers RDX
void DSPEmitter::Update_SR_Register(Gen::X64Reg val)
{
#ifdef _M_X64
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
// // 0x04
// if (_Value == 0) g_dsp.r[DSP_REG_SR] |= SR_ARITH_ZERO;
CMP(64, R(val), Imm8(0));
FixupBranch notZero = J_CC(CC_NZ);
OR(16, sr_reg, Imm16(SR_ARITH_ZERO));
SetJumpTarget(notZero);
// // 0x08
// if (_Value < 0) g_dsp.r[DSP_REG_SR] |= SR_SIGN;
CMP(64, R(val), Imm8(0));
FixupBranch greaterThanEqual = J_CC(CC_GE);
OR(16, sr_reg, Imm16(SR_SIGN));
SetJumpTarget(greaterThanEqual);
// // 0x10
// if (_Value != (s32)_Value) g_dsp.r[DSP_REG_SR] |= SR_OVER_S32;
MOVSX(64, 32, RDX, R(val));
CMP(64, R(RDX), R(val));
FixupBranch noOverS32 = J_CC(CC_E);
OR(16, sr_reg, Imm16(SR_OVER_S32));
SetJumpTarget(noOverS32);
// // 0x20 - Checks if top bits of m are equal
// if (((_Value & 0xc0000000) == 0) || ((_Value & 0xc0000000) == 0xc0000000))
AND(32, R(val), Imm32(0xc0000000));
CMP(32, R(val), Imm32(0));
FixupBranch zeroC = J_CC(CC_E);
CMP(32, R(val), Imm32(0xc0000000));
FixupBranch cC = J_CC(CC_NE);
SetJumpTarget(zeroC);
// g_dsp.r[DSP_REG_SR] |= SR_TOP2BITS;
OR(16, sr_reg, Imm16(SR_TOP2BITS));
SetJumpTarget(cC);
gpr.putReg(DSP_REG_SR);
#endif
}
// In: RAX: s64 _Value
// In: RCX: 1 = carry, 2 = overflow
// Clobbers RDX
void DSPEmitter::Update_SR_Register64(Gen::X64Reg val)
{
#ifdef _M_X64
// g_dsp.r[DSP_REG_SR] &= ~SR_CMP_MASK;
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
gpr.putReg(DSP_REG_SR);
Update_SR_Register(val);
#endif
}
// In: RAX: s64 _Value
// In: RCX: 1 = carry, 2 = overflow
// Clobbers RDX
void DSPEmitter::Update_SR_Register64_Carry(Gen::X64Reg val)
{
#ifdef _M_X64
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
// g_dsp.r[DSP_REG_SR] &= ~SR_CMP_MASK;
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
CMP(64, R(RCX), R(val));
// 0x01
// g_dsp.r[DSP_REG_SR] |= SR_CARRY;
// Carry = (acc>res)
FixupBranch noCarry = J_CC(CC_BE);
OR(16, sr_reg, Imm16(SR_CARRY));
SetJumpTarget(noCarry);
// 0x02 and 0x80
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW;
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW_STICKY;
// Overflow = ((acc ^ res) & (ax ^ res)) < 0
XOR(64, R(RCX), R(val));
XOR(64, R(RDX), R(val));
AND(64, R(RCX), R(RDX));
CMP(64, R(RCX), Imm8(0));
FixupBranch noOverflow = J_CC(CC_GE);
OR(16, sr_reg, Imm16(SR_OVERFLOW | SR_OVERFLOW_STICKY));
SetJumpTarget(noOverflow);
gpr.putReg(DSP_REG_SR);
Update_SR_Register(val);
#endif
}
// In: RAX: s64 _Value
// In: RCX: 1 = carry, 2 = overflow
// Clobbers RDX
void DSPEmitter::Update_SR_Register64_Carry2(Gen::X64Reg val)
{
#ifdef _M_X64
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
// g_dsp.r[DSP_REG_SR] &= ~SR_CMP_MASK;
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
CMP(64, R(RCX), R(val));
// 0x01
// g_dsp.r[DSP_REG_SR] |= SR_CARRY;
// Carry2 = (acc>=res)
FixupBranch noCarry2 = J_CC(CC_B);
OR(16, sr_reg, Imm16(SR_CARRY));
SetJumpTarget(noCarry2);
// 0x02 and 0x80
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW;
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW_STICKY;
// Overflow = ((acc ^ res) & (ax ^ res)) < 0
XOR(64, R(RCX), R(val));
XOR(64, R(RDX), R(val));
AND(64, R(RCX), R(RDX));
CMP(64, R(RCX), Imm8(0));
FixupBranch noOverflow = J_CC(CC_GE);
OR(16, sr_reg, Imm16(SR_OVERFLOW | SR_OVERFLOW_STICKY));
SetJumpTarget(noOverflow);
gpr.putReg(DSP_REG_SR);
Update_SR_Register();
#endif
}
//void DSPEmitter::Update_SR_Register16(s16 _Value, bool carry, bool overflow, bool overS32)
//{
// // 0x20 - Checks if top bits of m are equal
// if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
// {
// g_dsp.r[DSP_REG_SR] |= SR_TOP2BITS;
// }
//}
// In: RAX: s64 _Value
// In: RCX: 1 = carry, 2 = overflow
// Clobbers RDX
void DSPEmitter::Update_SR_Register16(Gen::X64Reg val)
{
#ifdef _M_X64
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
// // 0x04
// if (_Value == 0) g_dsp.r[DSP_REG_SR] |= SR_ARITH_ZERO;
CMP(64, R(val), Imm8(0));
FixupBranch notZero = J_CC(CC_NZ);
OR(16, sr_reg, Imm16(SR_ARITH_ZERO));
SetJumpTarget(notZero);
// // 0x08
// if (_Value < 0) g_dsp.r[DSP_REG_SR] |= SR_SIGN;
CMP(64, R(val), Imm8(0));
FixupBranch greaterThanEqual = J_CC(CC_GE);
OR(16, sr_reg, Imm16(SR_SIGN));
SetJumpTarget(greaterThanEqual);
// // 0x20 - Checks if top bits of m are equal
// if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
//AND(32, R(val), Imm32(0xc0000000));
SHR(16, R(val), Imm8(14));
CMP(16, R(val), Imm16(0));
FixupBranch nZero = J_CC(CC_NE);
OR(16, sr_reg, Imm16(SR_TOP2BITS));
FixupBranch cC = J();
SetJumpTarget(nZero);
CMP(16, R(val), Imm16(3));
FixupBranch notThree = J_CC(CC_NE);
// g_dsp.r[DSP_REG_SR] |= SR_TOP2BITS;
OR(16, sr_reg, Imm16(SR_TOP2BITS));
SetJumpTarget(notThree);
SetJumpTarget(cC);
gpr.putReg(DSP_REG_SR);
#endif
}
// In: RAX: s64 _Value
// In: RCX: 1 = carry, 2 = overflow
// Clobbers RDX
void DSPEmitter::Update_SR_Register16_OverS32(Gen::X64Reg val)
{
#ifdef _M_X64
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
// // 0x10
// if (_Value != (s32)_Value) g_dsp.r[DSP_REG_SR] |= SR_OVER_S32;
MOVSX(64, 32, RSI, R(val));
CMP(64, R(RSI), R(val));
FixupBranch noOverS32 = J_CC(CC_E);
OR(16, sr_reg, Imm16(SR_OVER_S32));
SetJumpTarget(noOverS32);
gpr.putReg(DSP_REG_SR);
// // 0x20 - Checks if top bits of m are equal
// if ((((u16)_Value >> 14) == 0) || (((u16)_Value >> 14) == 3))
//AND(32, R(val), Imm32(0xc0000000));
Update_SR_Register16(val);
#endif
}
//void DSPEmitter::Update_SR_LZ(bool value) {
// if (value == true)
// g_dsp.r[DSP_REG_SR] |= SR_LOGIC_ZERO;
// else
// g_dsp.r[DSP_REG_SR] &= ~SR_LOGIC_ZERO;
//}
//inline int GetMultiplyModifier()
//{
// return (g_dsp.r[DSP_REG_SR] & SR_MUL_MODIFY)?1:2;
//}
//inline bool isCarry() {
// return (g_dsp.r[DSP_REG_SR] & SR_CARRY) ? true : false;
//}
//inline bool isOverflow() {
// return (g_dsp.r[DSP_REG_SR] & SR_OVERFLOW) ? true : false;
//}
//inline bool isOverS32() {
// return (g_dsp.r[DSP_REG_SR] & SR_OVER_S32) ? true : false;
//}
//inline bool isLess() {
// return (!(g_dsp.r[DSP_REG_SR] & SR_OVERFLOW) != !(g_dsp.r[DSP_REG_SR] & SR_SIGN));
//}
//inline bool isZero() {
// return (g_dsp.r[DSP_REG_SR] & SR_ARITH_ZERO) ? true : false;
//}
//inline bool isLogicZero() {
// return (g_dsp.r[DSP_REG_SR] & SR_LOGIC_ZERO) ? true : false;
//}
//inline bool isConditionA() {
// return (((g_dsp.r[DSP_REG_SR] & SR_OVER_S32) || (g_dsp.r[DSP_REG_SR] & SR_TOP2BITS)) && !(g_dsp.r[DSP_REG_SR] & SR_ARITH_ZERO)) ? true : false;
//}
//see DSPCore.h for flags
//bool CheckCondition(u8 _Condition)
//{
// switch (_Condition & 0xf)
// {
// case 0xf: // Always true.
// return true;
// case 0x0: // GE - Greater Equal
// return !isLess();
// case 0x1: // L - Less
// return isLess();
// case 0x2: // G - Greater
// return !isLess() && !isZero();
// case 0x3: // LE - Less Equal
// return isLess() || isZero();
// case 0x4: // NZ - Not Zero
// return !isZero();
// case 0x5: // Z - Zero
// return isZero();
// case 0x6: // NC - Not carry
// return !isCarry();
// case 0x7: // C - Carry
// return isCarry();
// case 0x8: // ? - Not over s32
// return !isOverS32();
// case 0x9: // ? - Over s32
// return isOverS32();
// case 0xa: // ?
// return isConditionA();
// case 0xb: // ?
// return !isConditionA();
// case 0xc: // LNZ - Logic Not Zero
// return !isLogicZero();
// case 0xd: // LZ - Logic Zero
// return isLogicZero();
// case 0xe: // 0 - Overflow
// return isOverflow();
// default:
// return true;
// }
//}

View File

@ -0,0 +1,639 @@
// Copyright (C) 2010 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "../DSPMemoryMap.h"
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
using namespace Gen;
/* It is safe to directly write to the address registers as they are
neither read not written by any extendable opcode. The same is true
for memory accesses.
It probably even is safe to write to all registers except for
SR, ACx.x, AXx.x and PROD, which may be modified by the main op.
This code uses EBX to keep the values of the registers written by
the extended op so the main op can still access the old values.
storeIndex and storeIndex2 control where the lower and upper 16bits
of EBX are written to. Additionally, the upper 16bits can contain the
original SR so we can do sign extension in 40bit mode. There is only
the 'ld family of opcodes writing to two registers at the same time,
and those always are AXx.x, thus no need to leave space for SR for
sign extension.
*/
// DR $arR
// xxxx xxxx 0000 01rr
// Decrement addressing register $arR.
void DSPEmitter::dr(const UDSPInstruction opc) {
decrement_addr_reg(opc & 0x3);
}
// IR $arR
// xxxx xxxx 0000 10rr
// Increment addressing register $arR.
void DSPEmitter::ir(const UDSPInstruction opc) {
increment_addr_reg(opc & 0x3);
}
// NR $arR
// xxxx xxxx 0000 11rr
// Add corresponding indexing register $ixR to addressing register $arR.
void DSPEmitter::nr(const UDSPInstruction opc) {
u8 reg = opc & 0x3;
increase_addr_reg(reg);
}
// MV $axD.D, $acS.S
// xxxx xxxx 0001 ddss
// Move value of $acS.S to the $axD.D.
void DSPEmitter::mv(const UDSPInstruction opc)
{
u8 sreg = (opc & 0x3) + DSP_REG_ACL0;
u8 dreg = ((opc >> 2) & 0x3);
pushExtValueFromReg(dreg + DSP_REG_AXL0, sreg);
}
// S @$arD, $acS.S
// xxxx xxxx 001s s0dd
// Store value of $acS.S in the memory pointed by register $arD.
// Post increment register $arD.
void DSPEmitter::s(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
// u16 addr = g_dsp.r[dest];
dsp_op_read_reg(dreg, RAX, ZERO);
dsp_op_read_reg(sreg, RCX, ZERO);
// u16 val = g_dsp.r[src];
dmem_write();
increment_addr_reg(dreg);
}
// SN @$arD, $acS.S
// xxxx xxxx 001s s1dd
// Store value of register $acS.S in the memory pointed by register $arD.
// Add indexing register $ixD to register $arD.
void DSPEmitter::sn(const UDSPInstruction opc)
{
u8 dreg = opc & 0x3;
u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
dsp_op_read_reg(dreg, RAX, ZERO);
dsp_op_read_reg(sreg, RCX, ZERO);
dmem_write();
increase_addr_reg(dreg);
}
// L $axD.D, @$arS
// xxxx xxxx 01dd d0ss
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
// Post increment register $arS.
void DSPEmitter::l(const UDSPInstruction opc)
{
u8 sreg = opc & 0x3;
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0; //AX?.?, AC?.[LM]
pushExtValueFromMem(dreg, sreg);
if (dreg >= DSP_REG_ACM0) {
//save SR too, so we can decide later.
//even if only for one bit, can only
//store (up to) two registers in EBX,
//so store all of SR
#ifdef _M_IX86 // All32
MOV(16, R(EAX), M(&g_dsp.r.sr));
#else
MOV(64, R(RAX), ImmPtr(&g_dsp.r.sr));
MOV(16, R(RAX), MatR(RAX));
#endif
SHL(32, R(EAX), Imm8(16));
OR(32, R(EBX), R(EAX));
}
increment_addr_reg(sreg);
}
// LN $axD.D, @$arS
// xxxx xxxx 01dd d0ss
// Load $axD.D/$acD.D with value from memory pointed by register $arS.
// Add indexing register register $ixS to register $arS.
void DSPEmitter::ln(const UDSPInstruction opc)
{
u8 sreg = opc & 0x3;
u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
pushExtValueFromMem(dreg, sreg);
if (dreg >= DSP_REG_ACM0) {
//save SR too, so we can decide later.
//even if only for one bit, can only
//store (up to) two registers in EBX,
//so store all of SR
#ifdef _M_IX86 // All32
MOV(16, R(EAX), M(&g_dsp.r.sr));
#else
MOV(64, R(RAX), ImmPtr(&g_dsp.r.sr));
MOV(16, R(RAX), MatR(RAX));
#endif
SHL(32, R(EAX), Imm8(16));
OR(32, R(EBX), R(EAX));
}
increase_addr_reg(sreg);
}
// LS $axD.D, $acS.m
// xxxx xxxx 10dd 000s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Increment both $ar0 and $ar3.
void DSPEmitter::ls(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_op_read_reg(DSP_REG_AR3, RAX, ZERO);
get_acc_m(sreg, ECX, false);
dmem_write();
pushExtValueFromMem(dreg, DSP_REG_AR0);
increment_addr_reg(DSP_REG_AR3);
increment_addr_reg(DSP_REG_AR0);
}
// LSN $axD.D, $acS.m
// xxxx xxxx 10dd 010s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix0 to addressing
// register $ar0 and increment $ar3.
void DSPEmitter::lsn(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_op_read_reg(DSP_REG_AR3, RAX, ZERO);
get_acc_m(sreg, ECX, false);
dmem_write();
pushExtValueFromMem(dreg, DSP_REG_AR0);
increment_addr_reg(DSP_REG_AR3);
increase_addr_reg(DSP_REG_AR0);
}
// LSM $axD.D, $acS.m
// xxxx xxxx 10dd 100s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix3 to addressing
// register $ar3 and increment $ar0.
void DSPEmitter::lsm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_op_read_reg(DSP_REG_AR3, RAX, ZERO);
get_acc_m(sreg, ECX, false);
dmem_write();
pushExtValueFromMem(dreg, DSP_REG_AR0);
increase_addr_reg(DSP_REG_AR3);
increment_addr_reg(DSP_REG_AR0);
}
// LSMN $axD.D, $acS.m
// xxxx xxxx 10dd 110s
// Load register $axD.D with value from memory pointed by register
// $ar0. Store value from register $acS.m to memory location pointed by
// register $ar3. Add corresponding indexing register $ix0 to addressing
// register $ar0 and add corresponding indexing register $ix3 to addressing
// register $ar3.
void DSPEmitter::lsnm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_op_read_reg(DSP_REG_AR3, RAX, ZERO);
get_acc_m(sreg, ECX, false);
dmem_write();
pushExtValueFromMem(dreg, DSP_REG_AR0);
increase_addr_reg(DSP_REG_AR3);
increase_addr_reg(DSP_REG_AR0);
}
// SL $acS.m, $axD.D
// xxxx xxxx 10dd 001s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Increment both $ar0 and $ar3.
void DSPEmitter::sl(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_op_read_reg(DSP_REG_AR0, RAX, ZERO);
get_acc_m(sreg, ECX, false);
dmem_write();
pushExtValueFromMem(dreg, DSP_REG_AR3);
increment_addr_reg(DSP_REG_AR3);
increment_addr_reg(DSP_REG_AR0);
}
// SLN $acS.m, $axD.D
// xxxx xxxx 10dd 011s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
// and increment $ar3.
void DSPEmitter::sln(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_op_read_reg(DSP_REG_AR0, RAX, ZERO);
get_acc_m(sreg, ECX, false);
dmem_write();
pushExtValueFromMem(dreg, DSP_REG_AR3);
increment_addr_reg(DSP_REG_AR3);
increase_addr_reg(DSP_REG_AR0);
}
// SLM $acS.m, $axD.D
// xxxx xxxx 10dd 101s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix3 to addressing register $ar3
// and increment $ar0.
void DSPEmitter::slm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_op_read_reg(DSP_REG_AR0, RAX, ZERO);
get_acc_m(sreg, ECX, false);
dmem_write();
pushExtValueFromMem(dreg, DSP_REG_AR3);
increase_addr_reg(DSP_REG_AR3);
increment_addr_reg(DSP_REG_AR0);
}
// SLMN $acS.m, $axD.D
// xxxx xxxx 10dd 111s
// Store value from register $acS.m to memory location pointed by register
// $ar0. Load register $axD.D with value from memory pointed by register
// $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
// and add corresponding indexing register $ix3 to addressing register $ar3.
void DSPEmitter::slnm(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1;
u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
dsp_op_read_reg(DSP_REG_AR0, RAX, ZERO);
get_acc_m(sreg, ECX, false);
dmem_write();
pushExtValueFromMem(dreg, DSP_REG_AR3);
increase_addr_reg(DSP_REG_AR3);
increase_addr_reg(DSP_REG_AR0);
}
// LD $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 00ss
// example for "nx'ld $AX0.L, $AX1.L, @$AR3"
// Loads the word pointed by AR0 to AX0.H, then loads the word pointed by AR3
// to AX0.L. Increments AR0 and AR3. If AR0 and AR3 point into the same
// memory page (upper 6 bits of addr are the same -> games are not doing that!)
// then the value pointed by AR0 is loaded to BOTH AX0.H and AX0.L. If AR0
// points into an invalid memory page (ie 0x2000), then AX0.H keeps its old
// value. (not implemented yet) If AR3 points into an invalid memory page, then
// AX0.L gets the same value as AX0.H. (not implemented yet)
void DSPEmitter::ld(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
if (sreg != DSP_REG_AR3) {
pushExtValueFromMem((dreg << 1) + DSP_REG_AXL0, sreg);
// if (IsSameMemArea(g_dsp.r[sreg], g_dsp.r[DSP_REG_AR3])) {
dsp_op_read_reg(sreg, RSI, NONE);
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
SHR(16, R(ESI), Imm8(10));
SHR(16, R(EDI), Imm8(10));
DSPJitRegCache c(gpr);
CMP(16, R(ESI), R(EDI));
FixupBranch not_equal = J_CC(CC_NE,true);
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, sreg);
gpr.flushRegs(c);
FixupBranch after = J(true);
SetJumpTarget(not_equal); // else
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, DSP_REG_AR3);
gpr.flushRegs(c);
SetJumpTarget(after);
increment_addr_reg(sreg);
} else {
pushExtValueFromMem(rreg + DSP_REG_AXH0, dreg);
//if (IsSameMemArea(g_dsp.r[dreg], g_dsp.r[DSP_REG_AR3])) {
dsp_op_read_reg(dreg, RSI, NONE);
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
SHR(16, R(ESI), Imm8(10));
SHR(16, R(EDI), Imm8(10));
DSPJitRegCache c(gpr);
CMP(16, R(ESI), R(EDI));
FixupBranch not_equal = J_CC(CC_NE, true);
pushExtValueFromMem2(rreg + DSP_REG_AXL0, dreg);
gpr.flushRegs(c);
FixupBranch after = J(true); // else
SetJumpTarget(not_equal);
pushExtValueFromMem2(rreg + DSP_REG_AXL0, DSP_REG_AR3);
gpr.flushRegs(c);
SetJumpTarget(after);
increment_addr_reg(dreg);
}
increment_addr_reg(DSP_REG_AR3);
}
// LDN $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 01ss
void DSPEmitter::ldn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
if (sreg != DSP_REG_AR3) {
pushExtValueFromMem((dreg << 1) + DSP_REG_AXL0, sreg);
//if (IsSameMemArea(g_dsp.r[sreg], g_dsp.r[DSP_REG_AR3])) {
dsp_op_read_reg(sreg, RSI, NONE);
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
SHR(16, R(ESI), Imm8(10));
SHR(16, R(EDI), Imm8(10));
DSPJitRegCache c(gpr);
CMP(16, R(ESI), R(EDI));
FixupBranch not_equal = J_CC(CC_NE,true);
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, sreg);
gpr.flushRegs(c);
FixupBranch after = J(true);
SetJumpTarget(not_equal); // else
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, DSP_REG_AR3);
gpr.flushRegs(c);
SetJumpTarget(after);
increase_addr_reg(sreg);
} else {
pushExtValueFromMem(rreg + DSP_REG_AXH0, dreg);
//if (IsSameMemArea(g_dsp.r[dreg], g_dsp.r[DSP_REG_AR3])) {
dsp_op_read_reg(dreg, RSI, NONE);
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
SHR(16, R(ESI), Imm8(10));
SHR(16, R(EDI), Imm8(10));
DSPJitRegCache c(gpr);
CMP(16, R(ESI), R(EDI));
FixupBranch not_equal = J_CC(CC_NE,true);
pushExtValueFromMem2(rreg + DSP_REG_AXL0, dreg);
gpr.flushRegs(c);
FixupBranch after = J(true); // else
SetJumpTarget(not_equal);
pushExtValueFromMem2(rreg + DSP_REG_AXL0, DSP_REG_AR3);
gpr.flushRegs(c);
SetJumpTarget(after);
increase_addr_reg(dreg);
}
increment_addr_reg(DSP_REG_AR3);
}
// LDM $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 10ss
void DSPEmitter::ldm(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
if (sreg != DSP_REG_AR3) {
pushExtValueFromMem((dreg << 1) + DSP_REG_AXL0, sreg);
//if (IsSameMemArea(g_dsp.r[sreg], g_dsp.r[DSP_REG_AR3])) {
dsp_op_read_reg(sreg, RSI, NONE);
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
SHR(16, R(ESI), Imm8(10));
SHR(16, R(EDI), Imm8(10));
DSPJitRegCache c(gpr);
CMP(16, R(ESI), R(EDI));
FixupBranch not_equal = J_CC(CC_NE,true);
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, sreg);
gpr.flushRegs(c);
FixupBranch after = J(true);
SetJumpTarget(not_equal); // else
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, DSP_REG_AR3);
gpr.flushRegs(c);
SetJumpTarget(after);
increment_addr_reg(sreg);
} else {
pushExtValueFromMem(rreg + DSP_REG_AXH0, dreg);
//if (IsSameMemArea(g_dsp.r[dreg], g_dsp.r[DSP_REG_AR3])) {
dsp_op_read_reg(dreg, RSI, NONE);
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
SHR(16, R(ESI), Imm8(10));
SHR(16, R(EDI), Imm8(10));
DSPJitRegCache c(gpr);
CMP(16, R(ESI), R(EDI));
FixupBranch not_equal = J_CC(CC_NE,true);
pushExtValueFromMem2(rreg + DSP_REG_AXL0, dreg);
gpr.flushRegs(c);
FixupBranch after = J(true); // else
SetJumpTarget(not_equal);
pushExtValueFromMem2(rreg + DSP_REG_AXL0, DSP_REG_AR3);
gpr.flushRegs(c);
SetJumpTarget(after);
increment_addr_reg(dreg);
}
increase_addr_reg(DSP_REG_AR3);
}
// LDNM $ax0.d, $ax1.r, @$arS
// xxxx xxxx 11dr 11ss
void DSPEmitter::ldnm(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x1;
u8 rreg = (opc >> 4) & 0x1;
u8 sreg = opc & 0x3;
if (sreg != DSP_REG_AR3) {
pushExtValueFromMem((dreg << 1) + DSP_REG_AXL0, sreg);
//if (IsSameMemArea(g_dsp.r[sreg], g_dsp.r[DSP_REG_AR3])) {
dsp_op_read_reg(sreg, RSI, NONE);
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
SHR(16, R(ESI), Imm8(10));
SHR(16, R(EDI), Imm8(10));
DSPJitRegCache c(gpr);
CMP(16, R(ESI), R(EDI));
FixupBranch not_equal = J_CC(CC_NE,true);
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, sreg);
gpr.flushRegs(c);
FixupBranch after = J(true);
SetJumpTarget(not_equal); // else
pushExtValueFromMem2((rreg << 1) + DSP_REG_AXL1, DSP_REG_AR3);
gpr.flushRegs(c);
SetJumpTarget(after);
increase_addr_reg(sreg);
} else {
pushExtValueFromMem(rreg + DSP_REG_AXH0, dreg);
//if (IsSameMemArea(g_dsp.r[dreg], g_dsp.r[DSP_REG_AR3])) {
dsp_op_read_reg(dreg, RSI, NONE);
dsp_op_read_reg(DSP_REG_AR3, RDI, NONE);
SHR(16, R(ESI), Imm8(10));
SHR(16, R(EDI), Imm8(10));
DSPJitRegCache c(gpr);
CMP(16, R(ESI), R(EDI));
FixupBranch not_equal = J_CC(CC_NE,true);
pushExtValueFromMem2(rreg + DSP_REG_AXL0, dreg);
gpr.flushRegs(c);
FixupBranch after = J(true); // else
SetJumpTarget(not_equal);
pushExtValueFromMem2(rreg + DSP_REG_AXL0, DSP_REG_AR3);
gpr.flushRegs(c);
SetJumpTarget(after);
increase_addr_reg(dreg);
}
increase_addr_reg(DSP_REG_AR3);
}
// Push value from g_dsp.r[sreg] into EBX and stores the destinationindex in
// storeIndex
void DSPEmitter::pushExtValueFromReg(u16 dreg, u16 sreg) {
dsp_op_read_reg(sreg, RBX, ZERO);
storeIndex = dreg;
}
void DSPEmitter::pushExtValueFromMem(u16 dreg, u16 sreg) {
// u16 addr = g_dsp.r[addr];
dsp_op_read_reg(sreg, RCX, ZERO);
dmem_read();
MOVZX(32, 16, EBX, R(EAX));
storeIndex = dreg;
}
void DSPEmitter::pushExtValueFromMem2(u16 dreg, u16 sreg) {
// u16 addr = g_dsp.r[addr];
dsp_op_read_reg(sreg, RCX, ZERO);
dmem_read();
SHL(32, R(EAX), Imm8(16));
OR(32, R(EBX), R(EAX));
storeIndex2 = dreg;
}
void DSPEmitter::popExtValueToReg() {
// in practise, we rarely ever have a non-NX main op
// with an extended op, so the OR here is either
// not run (storeIndex == -1) or ends up OR'ing
// EBX with 0 (becoming the MOV we have here)
// nakee wants to keep it clean, so lets do that.
// [nakeee] the or case never happens in real
// [nakeee] it's just how the hardware works so we added it
if (storeIndex != -1) {
dsp_op_write_reg(storeIndex, RBX);
if (storeIndex >= DSP_REG_ACM0 && storeIndex2 == -1) {
TEST(32, R(EBX), Imm32(SR_40_MODE_BIT << 16));
FixupBranch not_40bit = J_CC(CC_Z);
DSPJitRegCache c(gpr);
//if (g_dsp.r[DSP_REG_SR] & SR_40_MODE_BIT)
//{
// Sign extend into whole accum.
//u16 val = g_dsp.r[reg];
MOVSX(32, 16, EAX, R(EBX));
SHR(32, R(EAX), Imm8(16));
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACH0] = (val & 0x8000) ? 0xFFFF : 0x0000;
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACL0] = 0;
set_acc_h(storeIndex - DSP_REG_ACM0, R(RAX));
set_acc_l(storeIndex - DSP_REG_ACM0, Imm16(0));
//}
gpr.flushRegs(c);
SetJumpTarget(not_40bit);
}
}
storeIndex = -1;
if (storeIndex2 != -1) {
SHR(32, R(EBX), Imm8(16));
dsp_op_write_reg(storeIndex2, RBX);
}
storeIndex2 = -1;
}
// This function is being called in the main op after all input regs were read
// and before it writes into any regs. This way we can always use bitwise or to
// apply the ext command output, because if the main op didn't change the value
// then 0 | ext output = ext output and if it did then bitwise or is still the
// right thing to do
//this is only needed as long as we do fallback for ext ops
void DSPEmitter::zeroWriteBackLog(const UDSPInstruction opc)
{
const DSPOPCTemplate *tinst = GetOpTemplate(opc);
// Call extended
if (!tinst->extended)
return;
if ((opc >> 12) == 0x3) {
if (! extOpTable[opc & 0x7F]->jitFunc)
{
SaveDSPRegs();
ABI_CallFunction((void*)::zeroWriteBackLog);
LoadDSPRegs();
}
} else {
if (! extOpTable[opc & 0xFF]->jitFunc)
{
SaveDSPRegs();
ABI_CallFunction((void*)::zeroWriteBackLog);
LoadDSPRegs();
}
}
return;
}

View File

@ -0,0 +1,294 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie and Tratax (c) 2004
#include "../DSPIntCCUtil.h"
#include "../DSPIntUtil.h"
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
using namespace Gen;
// SRS @M, $(0x18+S)
// 0010 1sss mmmm mmmm
// Move value from register $(0x18+D) to data memory pointed by address
// CR[0-7] | M. That is, the upper 8 bits of the address are the
// bottom 8 bits from CR, and the lower 8 bits are from the 8-bit immediate.
// Note: pc+=2 in duddie's doc seems wrong
void DSPEmitter::srs(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
//u16 addr = (g_dsp.r.cr << 8) | (opc & 0xFF);
dsp_op_read_reg(reg, RCX, ZERO);
dsp_op_read_reg(DSP_REG_CR, RAX, ZERO);
SHL(16, R(EAX), Imm8(8));
OR(8, R(EAX), Imm8(opc & 0xFF));
dmem_write();
}
// LRS $(0x18+D), @M
// 0010 0ddd mmmm mmmm
// Move value from data memory pointed by address CR[0-7] | M to register
// $(0x18+D). That is, the upper 8 bits of the address are the bottom 8 bits
// from CR, and the lower 8 bits are from the 8-bit immediate.
void DSPEmitter::lrs(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + 0x18;
//u16 addr = (g_dsp.r[DSP_REG_CR] << 8) | (opc & 0xFF);
dsp_op_read_reg(DSP_REG_CR, RCX, ZERO);
SHL(16, R(ECX), Imm8(8));
OR(8, R(ECX), Imm8(opc & 0xFF));
dmem_read();
dsp_op_write_reg(reg, RAX);
dsp_conditional_extend_accum(reg);
}
// LR $D, @M
// 0000 0000 110d dddd
// mmmm mmmm mmmm mmmm
// Move value from data memory pointed by address M to register $D.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lr(const UDSPInstruction opc)
{
int reg = opc & DSP_REG_MASK;
u16 address = dsp_imem_read(compilePC + 1);
dmem_read_imm(address);
dsp_op_write_reg(reg, EAX);
dsp_conditional_extend_accum(reg);
}
// SR @M, $S
// 0000 0000 111s ssss
// mmmm mmmm mmmm mmmm
// Store value from register $S to a memory pointed by address M.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::sr(const UDSPInstruction opc)
{
u8 reg = opc & DSP_REG_MASK;
u16 address = dsp_imem_read(compilePC + 1);
dsp_op_read_reg(reg, ECX);
dmem_write_imm(address);
}
// SI @M, #I
// 0001 0110 mmmm mmmm
// iiii iiii iiii iiii
// Store 16-bit immediate value I to a memory location pointed by address
// M (M is 8-bit value sign extended).
void DSPEmitter::si(const UDSPInstruction opc)
{
u16 address = (s8)opc;
u16 imm = dsp_imem_read(compilePC + 1);
MOV(32, R(ECX), Imm32((u32)imm));
dmem_write_imm(address);
}
// LRR $D, @$S
// 0001 1000 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lrr(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
dsp_op_read_reg(sreg, ECX);
dmem_read();
dsp_op_write_reg(dreg, EAX);
dsp_conditional_extend_accum(dreg);
}
// LRRD $D, @$S
// 0001 1000 1ssd dddd
// Move value from data memory pointed by addressing register $S toregister $D.
// Decrement register $S.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lrrd(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
dsp_op_read_reg(sreg, ECX);
dmem_read();
dsp_op_write_reg(dreg, EAX);
dsp_conditional_extend_accum(dreg);
decrement_addr_reg(sreg);
}
// LRRI $D, @$S
// 0001 1001 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Increment register $S.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lrri(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
dsp_op_read_reg(sreg, ECX);
dmem_read();
dsp_op_write_reg(dreg, EAX);
dsp_conditional_extend_accum(dreg);
increment_addr_reg(sreg);
}
// LRRN $D, @$S
// 0001 1001 1ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Add indexing register $(0x4+S) to register $S.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lrrn(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
u8 dreg = opc & 0x1f;
dsp_op_read_reg(sreg, ECX);
dmem_read();
dsp_op_write_reg(dreg, EAX);
dsp_conditional_extend_accum(dreg);
increase_addr_reg(sreg);
}
// SRR @$D, $S
// 0001 1010 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D.
// FIXME: Perform additional operation depending on source register.
void DSPEmitter::srr(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
dsp_op_read_reg(sreg, ECX);
dsp_op_read_reg(dreg, RAX, ZERO);
dmem_write();
}
// SRRD @$D, $S
// 0001 1010 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Decrement register $D.
// FIXME: Perform additional operation depending on source register.
void DSPEmitter::srrd(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
dsp_op_read_reg(sreg, ECX);
dsp_op_read_reg(dreg, RAX, ZERO);
dmem_write();
decrement_addr_reg(dreg);
}
// SRRI @$D, $S
// 0001 1011 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Increment register $D.
// FIXME: Perform additional operation depending on source register.
void DSPEmitter::srri(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
dsp_op_read_reg(sreg, ECX);
dsp_op_read_reg(dreg, RAX, ZERO);
dmem_write();
increment_addr_reg(dreg);
}
// SRRN @$D, $S
// 0001 1011 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Add DSP_REG_IX0 register to register $D.
// FIXME: Perform additional operation depending on source register.
void DSPEmitter::srrn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
u8 sreg = opc & 0x1f;
dsp_op_read_reg(sreg, ECX);
dsp_op_read_reg(dreg, RAX, ZERO);
dmem_write();
increase_addr_reg(dreg);
}
// ILRR $acD.m, @$arS
// 0000 001d 0001 00ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m.
void DSPEmitter::ilrr(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = (opc >> 8) & 1;
dsp_op_read_reg(reg, RCX, ZERO);
imem_read();
set_acc_m(dreg, R(RAX));
dsp_conditional_extend_accum(dreg);
}
// ILRRD $acD.m, @$arS
// 0000 001d 0001 01ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Decrement addressing register $arS.
void DSPEmitter::ilrrd(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = (opc >> 8) & 1;
dsp_op_read_reg(reg, RCX, ZERO);
imem_read();
set_acc_m(dreg, R(RAX));
dsp_conditional_extend_accum(dreg);
decrement_addr_reg(reg);
}
// ILRRI $acD.m, @$S
// 0000 001d 0001 10ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Increment addressing register $arS.
void DSPEmitter::ilrri(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = (opc >> 8) & 1;
dsp_op_read_reg(reg, RCX, ZERO);
imem_read();
set_acc_m(dreg, R(RAX));
dsp_conditional_extend_accum(dreg);
increment_addr_reg(reg);
}
// ILRRN $acD.m, @$arS
// 0000 001d 0001 11ss
// Move value from instruction memory pointed by addressing register
// $arS to mid accumulator register $acD.m. Add corresponding indexing
// register $ixS to addressing register $arS.
void DSPEmitter::ilrrn(const UDSPInstruction opc)
{
u16 reg = opc & 0x3;
u16 dreg = (opc >> 8) & 1;
dsp_op_read_reg(reg, RCX, ZERO);
imem_read();
set_acc_m(dreg, R(RAX));
dsp_conditional_extend_accum(dreg);
increase_addr_reg(reg);
}

View File

@ -0,0 +1,468 @@
// Copyright (C) 2010 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "../DSPIntUtil.h"
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
using namespace Gen;
//clobbers:
//EAX = (s8)g_dsp.reg_stack_ptr[stack_reg]
//CX = g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]]
//expects:
//R11 = &g_dsp.r
void DSPEmitter::dsp_reg_stack_push(int stack_reg)
{
//g_dsp.reg_stack_ptr[stack_reg]++;
//g_dsp.reg_stack_ptr[stack_reg] &= DSP_STACK_MASK;
#ifdef _M_IX86 // All32
MOV(8, R(AL), M(&g_dsp.reg_stack_ptr[stack_reg]));
#else
MOV(8, R(AL), MDisp(R11, PtrOffset(&g_dsp.reg_stack_ptr[stack_reg],
&g_dsp.r)));
#endif
ADD(8, R(AL), Imm8(1));
AND(8, R(AL), Imm8(DSP_STACK_MASK));
#ifdef _M_IX86 // All32
MOV(8, M(&g_dsp.reg_stack_ptr[stack_reg]), R(AL));
#else
MOV(8, MDisp(R11, PtrOffset(&g_dsp.reg_stack_ptr[stack_reg],
&g_dsp.r)), R(AL));
#endif
//g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]] = g_dsp.r[DSP_REG_ST0 + stack_reg];
#ifdef _M_IX86 // All32
MOV(16, R(CX), M(&g_dsp.r.st[stack_reg]));
MOVZX(32, 8, EAX, R(AL));
MOV(16, MComplex(EAX, EAX, 1, (u32)&g_dsp.reg_stack[stack_reg][0]), R(CX));
#else
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
MOV(16, R(CX), MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])));
MOVZX(64, 8, RAX, R(AL));
MOV(16, MComplex(R11, RAX, 2,
PtrOffset(&g_dsp.reg_stack[stack_reg][0],&g_dsp.r)),
R(CX));
#endif
}
//clobbers:
//EAX = (s8)g_dsp.reg_stack_ptr[stack_reg]
//CX = g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]]
//expects:
//R11 = &g_dsp.r
void DSPEmitter::dsp_reg_stack_pop(int stack_reg)
{
//g_dsp.r[DSP_REG_ST0 + stack_reg] = g_dsp.reg_stack[stack_reg][g_dsp.reg_stack_ptr[stack_reg]];
#ifdef _M_IX86 // All32
MOV(8, R(AL), M(&g_dsp.reg_stack_ptr[stack_reg]));
#else
MOV(8, R(AL),
MDisp(R11, PtrOffset(&g_dsp.reg_stack_ptr[stack_reg],&g_dsp.r)));
#endif
#ifdef _M_IX86 // All32
MOVZX(32, 8, EAX, R(AL));
MOV(16, R(CX), MComplex(EAX, EAX, 1, (u32)&g_dsp.reg_stack[stack_reg][0]));
MOV(16, M(&g_dsp.r.st[stack_reg]), R(CX));
#else
MOVZX(64, 8, RAX, R(AL));
MOV(16, R(CX), MComplex(R11, RAX, 2,
PtrOffset(&g_dsp.reg_stack[stack_reg][0],
&g_dsp.r)));
MOV(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])), R(CX));
#endif
//g_dsp.reg_stack_ptr[stack_reg]--;
//g_dsp.reg_stack_ptr[stack_reg] &= DSP_STACK_MASK;
SUB(8, R(AL), Imm8(1));
AND(8, R(AL), Imm8(DSP_STACK_MASK));
#ifdef _M_IX86 // All32
MOV(8, M(&g_dsp.reg_stack_ptr[stack_reg]), R(AL));
#else
MOV(8, MDisp(R11, PtrOffset(&g_dsp.reg_stack_ptr[stack_reg],&g_dsp.r)),
R(AL));
#endif
}
void DSPEmitter::dsp_reg_store_stack(int stack_reg, Gen::X64Reg host_sreg)
{
if (host_sreg != EDX) {
MOV(16, R(EDX), R(host_sreg));
}
dsp_reg_stack_push(stack_reg);
//g_dsp.r[DSP_REG_ST0 + stack_reg] = val;
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.r.st[stack_reg]), R(EDX));
#else
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
MOV(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])), R(EDX));
#endif
}
void DSPEmitter::dsp_reg_load_stack(int stack_reg, Gen::X64Reg host_dreg)
{
//u16 val = g_dsp.r[DSP_REG_ST0 + stack_reg];
#ifdef _M_IX86 // All32
MOV(16, R(EDX), M(&g_dsp.r.st[stack_reg]));
#else
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
MOV(16, R(EDX), MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])));
#endif
dsp_reg_stack_pop(stack_reg);
if (host_dreg != EDX) {
MOV(16, R(host_dreg), R(EDX));
}
}
void DSPEmitter::dsp_reg_store_stack_imm(int stack_reg, u16 val)
{
dsp_reg_stack_push(stack_reg);
//g_dsp.r[DSP_REG_ST0 + stack_reg] = val;
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.r.st[stack_reg]), Imm16(val));
#else
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
MOV(16, MDisp(R11, STRUCT_OFFSET(g_dsp.r, st[stack_reg])), Imm16(val));
#endif
}
void DSPEmitter::dsp_op_write_reg(int reg, Gen::X64Reg host_sreg)
{
switch (reg & 0x1f) {
// 8-bit sign extended registers.
case DSP_REG_ACH0:
case DSP_REG_ACH1:
gpr.writeReg(reg, R(host_sreg));
break;
// Stack registers.
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
dsp_reg_store_stack(reg - DSP_REG_ST0, host_sreg);
break;
default:
gpr.writeReg(reg, R(host_sreg));
break;
}
}
void DSPEmitter::dsp_op_write_reg_imm(int reg, u16 val)
{
switch (reg & 0x1f) {
// 8-bit sign extended registers. Should look at prod.h too...
case DSP_REG_ACH0:
case DSP_REG_ACH1:
gpr.writeReg(reg, Imm16((u16)(s16)(s8)(u8)val));
break;
// Stack registers.
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
dsp_reg_store_stack_imm(reg - DSP_REG_ST0, val);
break;
default:
gpr.writeReg(reg, Imm16(val));
break;
}
}
void DSPEmitter::dsp_conditional_extend_accum(int reg)
{
switch (reg)
{
case DSP_REG_ACM0:
case DSP_REG_ACM1:
{
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
DSPJitRegCache c(gpr);
TEST(16, sr_reg, Imm16(SR_40_MODE_BIT));
FixupBranch not_40bit = J_CC(CC_Z,true);
//if (g_dsp.r[DSP_REG_SR] & SR_40_MODE_BIT)
//{
// Sign extend into whole accum.
//u16 val = g_dsp.r[reg];
get_acc_m(reg - DSP_REG_ACM0, EAX);
SHR(32, R(EAX), Imm8(16));
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACH0] = (val & 0x8000) ? 0xFFFF : 0x0000;
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACL0] = 0;
set_acc_h(reg - DSP_REG_ACM0, R(RAX));
set_acc_l(reg - DSP_REG_ACM0, Imm16(0));
//}
gpr.flushRegs(c);
SetJumpTarget(not_40bit);
gpr.putReg(DSP_REG_SR);
}
}
}
void DSPEmitter::dsp_conditional_extend_accum_imm(int reg, u16 val)
{
switch (reg)
{
case DSP_REG_ACM0:
case DSP_REG_ACM1:
{
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
DSPJitRegCache c(gpr);
TEST(16, sr_reg, Imm16(SR_40_MODE_BIT));
FixupBranch not_40bit = J_CC(CC_Z);
//if (g_dsp.r[DSP_REG_SR] & SR_40_MODE_BIT)
//{
// Sign extend into whole accum.
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACH0] = (val & 0x8000) ? 0xFFFF : 0x0000;
//g_dsp.r[reg - DSP_REG_ACM0 + DSP_REG_ACL0] = 0;
set_acc_h(reg - DSP_REG_ACM0, Imm16((val & 0x8000)?0xffff:0x0000));
set_acc_l(reg - DSP_REG_ACM0, Imm16(0));
//}
gpr.flushRegs(c);
SetJumpTarget(not_40bit);
gpr.putReg(DSP_REG_SR);
}
}
}
void DSPEmitter::dsp_op_read_reg(int reg, Gen::X64Reg host_dreg, DSPJitSignExtend extend)
{
switch (reg & 0x1f) {
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
dsp_reg_load_stack(reg - DSP_REG_ST0, host_dreg);
switch(extend) {
case SIGN:
#ifdef _M_IX86 // All32
MOVSX(32, 16, host_dreg, R(host_dreg));
#else
MOVSX(64, 16, host_dreg, R(host_dreg));
#endif
break;
case ZERO:
#ifdef _M_IX86 // All32
MOVZX(32, 16, host_dreg, R(host_dreg));
#else
MOVZX(64, 16, host_dreg, R(host_dreg));
#endif
break;
case NONE:
default:
break;
}
return;
default:
gpr.readReg(reg, host_dreg, extend);
return;
}
}
// MRR $D, $S
// 0001 11dd ddds ssss
// Move value from register $S to register $D.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::mrr(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1f;
u8 dreg = (opc >> 5) & 0x1f;
dsp_op_read_reg(sreg, EDX);
dsp_op_write_reg(dreg, EDX);
dsp_conditional_extend_accum(dreg);
}
// LRI $D, #I
// 0000 0000 100d dddd
// iiii iiii iiii iiii
// Load immediate value I to register $D.
//
// DSPSpy discovery: This, and possibly other instructions that load a
// register, has a different behaviour in S40 mode if loaded to AC0.M: The
// value gets sign extended to the whole accumulator! This does not happen in
// S16 mode.
void DSPEmitter::lri(const UDSPInstruction opc)
{
u8 reg = opc & DSP_REG_MASK;
u16 imm = dsp_imem_read(compilePC+1);
dsp_op_write_reg_imm(reg, imm);
dsp_conditional_extend_accum_imm(reg, imm);
}
// LRIS $(0x18+D), #I
// 0000 1ddd iiii iiii
// Load immediate value I (8-bit sign extended) to accumulator register.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lris(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
u16 imm = (s8)opc;
dsp_op_write_reg_imm(reg, imm);
dsp_conditional_extend_accum_imm(reg, imm);
}
//----
// NX
// 1000 -000 xxxx xxxx
// No operation, but can be extended with extended opcode.
// This opcode is supposed to do nothing - it's used if you want to use
// an opcode extension but not do anything. At least according to duddie.
void DSPEmitter::nx(const UDSPInstruction opc)
{
zeroWriteBackLog(opc);
}
//----
// DAR $arD
// 0000 0000 0000 01dd
// Decrement address register $arD.
void DSPEmitter::dar(const UDSPInstruction opc)
{
// g_dsp.r[opc & 0x3] = dsp_decrement_addr_reg(opc & 0x3);
decrement_addr_reg(opc & 0x3);
}
// IAR $arD
// 0000 0000 0000 10dd
// Increment address register $arD.
void DSPEmitter::iar(const UDSPInstruction opc)
{
// g_dsp.r[opc & 0x3] = dsp_increment_addr_reg(opc & 0x3);
increment_addr_reg(opc & 0x3);
}
// SUBARN $arD
// 0000 0000 0000 11dd
// Subtract indexing register $ixD from an addressing register $arD.
// used only in IPL-NTSC ucode
void DSPEmitter::subarn(const UDSPInstruction opc)
{
// u8 dreg = opc & 0x3;
// g_dsp.r[dreg] = dsp_decrease_addr_reg(dreg, (s16)g_dsp.r[DSP_REG_IX0 + dreg]);
decrease_addr_reg(opc & 0x3);
}
// ADDARN $arD, $ixS
// 0000 0000 0001 ssdd
// Adds indexing register $ixS to an addressing register $arD.
// It is critical for the Zelda ucode that this one wraps correctly.
void DSPEmitter::addarn(const UDSPInstruction opc)
{
// u8 dreg = opc & 0x3;
// u8 sreg = (opc >> 2) & 0x3;
// g_dsp.r[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r[DSP_REG_IX0 + sreg]);
// From looking around it is always called with the matching index register
increase_addr_reg(opc & 0x3);
}
//----
void DSPEmitter::setCompileSR(u16 bit) {
// g_dsp.r[DSP_REG_SR] |= bit
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
OR(16, sr_reg, Imm16(bit));
gpr.putReg(DSP_REG_SR);
compileSR |= bit;
}
void DSPEmitter::clrCompileSR(u16 bit) {
// g_dsp.r[DSP_REG_SR] &= bit
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
AND(16, sr_reg, Imm16(~bit));
gpr.putReg(DSP_REG_SR);
compileSR &= ~bit;
}
// SBCLR #I
// 0001 0011 aaaa aiii
// bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void DSPEmitter::sbclr(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
clrCompileSR(1 << bit);
}
// SBSET #I
// 0001 0010 aaaa aiii
// Set bit of status register $sr. Bit number is calculated by adding 6 to
// immediate value I.
void DSPEmitter::sbset(const UDSPInstruction opc)
{
u8 bit = (opc & 0x7) + 6;
setCompileSR(1 << bit);
}
// 1000 1bbb xxxx xxxx, bbb >= 010
// This is a bunch of flag setters, flipping bits in SR. So far so good,
// but it's harder to know exactly what effect they have.
void DSPEmitter::srbith(const UDSPInstruction opc)
{
zeroWriteBackLog(opc);
switch ((opc >> 8) & 0xf)
{
// M0/M2 change the multiplier mode (it can multiply by 2 for free).
case 0xa: // M2
clrCompileSR(SR_MUL_MODIFY);
break;
case 0xb: // M0
setCompileSR(SR_MUL_MODIFY);
break;
// If set, treat multiplicands as unsigned.
// If clear, treat them as signed.
case 0xc: // CLR15
clrCompileSR(SR_MUL_UNSIGNED);
break;
case 0xd: // SET15
setCompileSR(SR_MUL_UNSIGNED);
break;
// Automatic 40-bit sign extension when loading ACx.M.
// SET40 changes something very important: see the LRI instruction above.
case 0xe: // SET16 (CLR40)
clrCompileSR(SR_40_MODE_BIT);
break;
case 0xf: // SET40
setCompileSR(SR_40_MODE_BIT);
break;
default:
break;
}
}

View File

@ -0,0 +1,885 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Additional copyrights go to Duddie and Tratax (c) 2004
// Multiplier and product register control
#include "../DSPIntUtil.h"
#include "../DSPEmitter.h"
#include "../DSPAnalyzer.h"
#ifdef _M_X64
#include "DSPJitUtil.h"
#endif
#include "x64Emitter.h"
#include "ABI.h"
using namespace Gen;
// Returns s64 in RAX
// In: RSI = s16 a, RDI = s16 b
void DSPEmitter::multiply()
{
#ifdef _M_X64
// prod = (s16)a * (s16)b; //signed
MOV(64, R(EAX), R(RDI));
IMUL(64, R(ESI));
// Conditionally multiply by 2.
// if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
TEST(16, sr_reg, Imm16(SR_MUL_MODIFY));
FixupBranch noMult2 = J_CC(CC_NZ);
// prod <<= 1;
SHL(64, R(EAX), Imm8(1));
SetJumpTarget(noMult2);
gpr.putReg(DSP_REG_SR);
// return prod;
#endif
}
// Returns s64 in RAX
// Clobbers RDX
void DSPEmitter::multiply_add()
{
// s64 prod = dsp_get_long_prod() + dsp_get_multiply_prod(a, b, sign);
multiply();
MOV(64, R(RDX), R(RAX));
get_long_prod();
ADD(64, R(RAX), R(RDX));
// return prod;
}
// Returns s64 in RAX
// Clobbers RDX
void DSPEmitter::multiply_sub()
{
// s64 prod = dsp_get_long_prod() - dsp_get_multiply_prod(a, b, sign);
multiply();
MOV(64, R(RDX), R(RAX));
get_long_prod();
SUB(64, R(RAX), R(RDX));
// return prod;
}
// Only MULX family instructions have unsigned/mixed support.
// Returns s64 in EAX
// In: RSI = s16 a, RDI = s16 b
// Returns s64 in RAX
void DSPEmitter::multiply_mulx(u8 axh0, u8 axh1)
{
// s64 result;
// if ((axh0==0) && (axh1==0))
// result = dsp_multiply(val1, val2, 1); // unsigned support ON if both ax?.l regs are used
// else if ((axh0==0) && (axh1==1))
// result = dsp_multiply(val1, val2, 2); // mixed support ON (u16)axl.0 * (s16)axh.1
// else if ((axh0==1) && (axh1==0))
// result = dsp_multiply(val2, val1, 2); // mixed support ON (u16)axl.1 * (s16)axh.0
// else
// result = dsp_multiply(val1, val2, 0); // unsigned support OFF if both ax?.h regs are used
// if ((sign == 1) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) //unsigned
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
TEST(16, sr_reg, Imm16(SR_MUL_UNSIGNED));
FixupBranch unsignedMul = J_CC(CC_NZ);
// prod = (s16)a * (s16)b; //signed
MOVSX(64, 16, RAX, R(RDI));
IMUL(64, R(RSI));
FixupBranch signedMul = J();
SetJumpTarget(unsignedMul);
if ((axh0==0) && (axh1==0))
{
// unsigned support ON if both ax?.l regs are used
// prod = (u32)(a * b);
MOVZX(64, 16, RSI, R(RSI));
MOVZX(64, 16, RAX, R(RDI));
MUL(64, R(RSI));
}
else if ((axh0==0) && (axh1==1))
{
// mixed support ON (u16)axl.0 * (s16)axh.1
// prod = a * (s16)b;
MOVZX(64, 16, RAX, R(RSI));
IMUL(64, R(RDI));
}
else if ((axh0==1) && (axh1==0))
{
// mixed support ON (u16)axl.1 * (s16)axh.0
// prod = (s16)a * b;
MOVZX(64, 16, RAX, R(RDI));
IMUL(64, R(RSI));
}
else
{
// unsigned support OFF if both ax?.h regs are used
// prod = (s16)a * (s16)b; //signed
MOVSX(64, 16, RAX, R(RDI));
IMUL(64, R(RSI));
}
SetJumpTarget(signedMul);
// Conditionally multiply by 2.
// if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
TEST(16, sr_reg, Imm16(SR_MUL_MODIFY));
FixupBranch noMult2 = J_CC(CC_NZ);
// prod <<= 1;
SHL(64, R(RAX), Imm8(1));
SetJumpTarget(noMult2);
gpr.putReg(DSP_REG_SR);
// return prod;
}
//----
// CLRP
// 1000 0100 xxxx xxxx
// Clears product register $prod.
// Magic numbers taken from duddie's doc
// 00ff_(fff0 + 0010)_0000 = 0100_0000_0000, conveniently, lower 40bits = 0
// It's not ok, to just zero all of them, correct values should be set because of
// direct use of prod regs by AX/AXWII (look @that part of ucode).
void DSPEmitter::clrp(const UDSPInstruction opc)
{
#ifdef _M_X64
// g_dsp.r[DSP_REG_PRODL] = 0x0000;
// g_dsp.r[DSP_REG_PRODM] = 0xfff0;
// g_dsp.r[DSP_REG_PRODH] = 0x00ff;
// g_dsp.r[DSP_REG_PRODM2] = 0x0010;
//64bit move to memory does not work. use 2 32bits
MOV(32, MDisp(R11, STRUCT_OFFSET(g_dsp.r, prod.val)+0), Imm32(0xfff00000U));
MOV(32, MDisp(R11, STRUCT_OFFSET(g_dsp.r, prod.val)+4), Imm32(0x001000ffU));
#else
Default(opc);
#endif
}
// TSTPROD
// 1000 0101 xxxx xxxx
// Test prod regs value.
// flags out: --xx xx0x
void DSPEmitter::tstprod(const UDSPInstruction opc)
{
#ifdef _M_X64
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
// s64 prod = dsp_get_long_prod();
get_long_prod();
// Update_SR_Register64(prod);
Update_SR_Register64();
}
#else
Default(opc);
#endif
}
//----
// MOVP $acD
// 0110 111d xxxx xxxx
// Moves multiply product from $prod register to accumulator $acD register.
// flags out: --xx xx0x
void DSPEmitter::movp(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 dreg = (opc >> 8) & 0x1;
// s64 acc = dsp_get_long_prod();
get_long_prod();
// dsp_set_long_acc(dreg, acc);
set_long_acc(dreg);
// Update_SR_Register64(acc);
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64();
}
#else
Default(opc);
#endif
}
// MOVNP $acD
// 0111 111d xxxx xxxx
// Moves negative of multiply product from $prod register to accumulator
// $acD register.
// flags out: --xx xx0x
void DSPEmitter::movnp(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 dreg = (opc >> 8) & 0x1;
// s64 acc = -dsp_get_long_prod();
get_long_prod();
NEG(64, R(EAX));
// dsp_set_long_acc(dreg, acc);
set_long_acc(dreg);
// Update_SR_Register64(acc);
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64();
}
#else
Default(opc);
#endif
}
// MOVPZ $acD
// 1111 111d xxxx xxxx
// Moves multiply product from $prod register to accumulator $acD
// register and sets (rounds) $acD.l to 0
// flags out: --xx xx0x
void DSPEmitter::movpz(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 dreg = (opc >> 8) & 0x01;
// s64 acc = dsp_get_long_prod_round_prodl();
get_long_prod_round_prodl();
// dsp_set_long_acc(dreg, acc);
set_long_acc(dreg);
// Update_SR_Register64(acc);
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64();
}
#else
Default(opc);
#endif
}
// ADDPAXZ $acD, $axS
// 1111 10sd xxxx xxxx
// Adds secondary accumulator $axS to product register and stores result
// in accumulator register. Low 16-bits of $acD ($acD.l) are set (round) to 0.
// flags out: --xx xx0x
void DSPEmitter::addpaxz(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 dreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
// s64 ax = dsp_get_long_acx(sreg);
get_long_acx(sreg, RCX);
MOV(64, R(RDI), R(RCX));
// s64 res = prod + (ax & ~0xffff);
MOV(64, R(RDX), Imm64(~0xffff));
AND(64, R(RDI), R(RDX));
// s64 prod = dsp_get_long_prod_round_prodl();
get_long_prod_round_prodl();
ADD(64, R(RAX), R(RDI));
// s64 oldprod = dsp_get_long_prod();
// dsp_set_long_acc(dreg, res);
// res = dsp_get_long_acc(dreg);
// Update_SR_Register64(res, isCarry(oldprod, res), false);
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
get_long_prod(RDX);
MOV(64, R(RSI), R(RAX));
set_long_acc(dreg, RSI);
Update_SR_Register64_Carry();
}
else
{
set_long_acc(dreg, RAX);
}
#else
Default(opc);
#endif
}
//----
// MULAXH
// 1000 0011 xxxx xxxx
// Multiply $ax0.h by $ax0.h
void DSPEmitter::mulaxh(const UDSPInstruction opc)
{
#ifdef _M_X64
// s64 prod = dsp_multiply(dsp_get_ax_h(0), dsp_get_ax_h(0));
dsp_op_read_reg(DSP_REG_AXH0, RSI, SIGN);
MOV(64, R(RDI), R(RSI));
multiply();
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}
//----
// MUL $axS.l, $axS.h
// 1001 s000 xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed).
void DSPEmitter::mul(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 sreg = (opc >> 11) & 0x1;
// u16 axl = dsp_get_ax_l(sreg);
dsp_op_read_reg(DSP_REG_AXL0+sreg, RSI, SIGN);
// u16 axh = dsp_get_ax_h(sreg);
dsp_op_read_reg(DSP_REG_AXH0+sreg, RDI, SIGN);
// s64 prod = dsp_multiply(axh, axl);
multiply();
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}
// MULAC $axS.l, $axS.h, $acR
// 1001 s10r xxxx xxxx
// Add product register to accumulator register $acR. Multiply low part
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
// accumulator $axS (treat them both as signed).
// flags out: --xx xx0x
void DSPEmitter::mulac(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 rreg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 11) & 0x1;
// s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
get_long_acc(rreg);
MOV(64, R(RDX), R(RAX));
get_long_prod();
ADD(64, R(RAX), R(RDX));
PUSH(64, R(RAX));
// u16 axl = dsp_get_ax_l(sreg);
dsp_op_read_reg(DSP_REG_AXL0+sreg, RSI, SIGN);
// u16 axh = dsp_get_ax_h(sreg);
dsp_op_read_reg(DSP_REG_AXH0+sreg, RDI, SIGN);
// s64 prod = dsp_multiply(axl, axh);
multiply();
// dsp_set_long_prod(prod);
set_long_prod();
// dsp_set_long_acc(rreg, acc);
POP(64, R(RAX));
set_long_acc(rreg);
// Update_SR_Register64(dsp_get_long_acc(rreg));
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64();
}
#else
Default(opc);
#endif
}
// MULMV $axS.l, $axS.h, $acR
// 1001 s11r xxxx xxxx
// Move product register to accumulator register $acR. Multiply low part
// $axS.l of secondary accumulator $axS by high part $axS.h of secondary
// accumulator $axS (treat them both as signed).
// flags out: --xx xx0x
void DSPEmitter::mulmv(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 rreg = (opc >> 8) & 0x1;
// s64 acc = dsp_get_long_prod();
get_long_prod();
PUSH(64, R(RAX));
mul(opc);
// dsp_set_long_acc(rreg, acc);
POP(64, R(RAX));
set_long_acc(rreg);
// Update_SR_Register64(dsp_get_long_acc(rreg));
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64();
}
#else
Default(opc);
#endif
}
// MULMVZ $axS.l, $axS.h, $acR
// 1001 s01r xxxx xxxx
// Move product register to accumulator register $acR and clear (round) low part
// of accumulator register $acR.l. Multiply low part $axS.l of secondary
// accumulator $axS by high part $axS.h of secondary accumulator $axS (treat
// them both as signed).
// flags out: --xx xx0x
void DSPEmitter::mulmvz(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 rreg = (opc >> 8) & 0x1;
// s64 acc = dsp_get_long_prod_round_prodl();
get_long_prod_round_prodl(RDX);
// dsp_set_long_acc(rreg, acc);
set_long_acc(rreg, RDX);
mul(opc);
// Update_SR_Register64(dsp_get_long_acc(rreg));
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64(RDX);
}
#else
Default(opc);
#endif
}
//----
// MULX $ax0.S, $ax1.T
// 101s t000 xxxx xxxx
// Multiply one part $ax0 by one part $ax1.
// Part is selected by S and T bits. Zero selects low part, one selects high part.
void DSPEmitter::mulx(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 treg = ((opc >> 11) & 0x1);
u8 sreg = ((opc >> 12) & 0x1);
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
// s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
multiply_mulx(sreg, treg);
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}
// MULXAC $ax0.S, $ax1.T, $acR
// 101s t01r xxxx xxxx
// Add product register to accumulator register $acR. Multiply one part
// $ax0 by one part $ax1. Part is selected by S and
// T bits. Zero selects low part, one selects high part.
// flags out: --xx xx0x
void DSPEmitter::mulxac(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
// s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
get_long_acc(rreg, RCX);
get_long_prod();
ADD(64, R(RCX), R(RAX));
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
// s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
multiply_mulx(sreg, treg);
// dsp_set_long_prod(prod);
set_long_prod();
// dsp_set_long_acc(rreg, acc);
set_long_acc(rreg, RCX);
// Update_SR_Register64(dsp_get_long_acc(rreg));
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64(RCX);
}
#else
Default(opc);
#endif
}
// MULXMV $ax0.S, $ax1.T, $acR
// 101s t11r xxxx xxxx
// Move product register to accumulator register $acR. Multiply one part
// $ax0 by one part $ax1. Part is selected by S and
// T bits. Zero selects low part, one selects high part.
// flags out: --xx xx0x
void DSPEmitter::mulxmv(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 rreg = ((opc >> 8) & 0x1);
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
// s64 acc = dsp_get_long_prod();
get_long_prod(RCX);
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
// s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
multiply_mulx(sreg, treg);
// dsp_set_long_prod(prod);
set_long_prod();
// dsp_set_long_acc(rreg, acc);
set_long_acc(rreg, RCX);
// Update_SR_Register64(dsp_get_long_acc(rreg));
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64(RCX);
}
#else
Default(opc);
#endif
}
// MULXMV $ax0.S, $ax1.T, $acR
// 101s t01r xxxx xxxx
// Move product register to accumulator register $acR and clear (round) low part
// of accumulator register $acR.l. Multiply one part $ax0 by one part $ax1
// Part is selected by S and T bits. Zero selects low part,
// one selects high part.
// flags out: --xx xx0x
void DSPEmitter::mulxmvz(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
// s64 acc = dsp_get_long_prod_round_prodl();
get_long_prod_round_prodl(RCX);
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
// s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
multiply_mulx(sreg, treg);
// dsp_set_long_prod(prod);
set_long_prod();
// dsp_set_long_acc(rreg, acc);
set_long_acc(rreg, RCX);
// Update_SR_Register64(dsp_get_long_acc(rreg));
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64(RCX);
}
#else
Default(opc);
#endif
}
//----
// MULC $acS.m, $axT.h
// 110s t000 xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
// secondary accumulator $axS (treat them both as signed).
void DSPEmitter::mulc(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
// u16 accm = dsp_get_acc_m(sreg);
get_acc_m(sreg, ESI);
// u16 axh = dsp_get_ax_h(treg);
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
// s64 prod = dsp_multiply(accm, axh);
multiply();
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}
// MULCAC $acS.m, $axT.h, $acR
// 110s t10r xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axS.h of
// secondary accumulator $axS (treat them both as signed). Add product
// register before multiplication to accumulator $acR.
// flags out: --xx xx0x
void DSPEmitter::mulcac(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
// s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
get_long_acc(rreg);
MOV(64, R(RDX), R(RAX));
get_long_prod();
ADD(64, R(RAX), R(RDX));
PUSH(64, R(RAX));
// u16 accm = dsp_get_acc_m(sreg);
get_acc_m(sreg, ESI);
// u16 axh = dsp_get_ax_h(treg);
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
// s64 prod = dsp_multiply(accm, axh);
multiply();
// dsp_set_long_prod(prod);
set_long_prod();
// dsp_set_long_acc(rreg, acc);
POP(64, R(RAX));
set_long_acc(rreg);
// Update_SR_Register64(dsp_get_long_acc(rreg));
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64();
}
#else
Default(opc);
#endif
}
// MULCMV $acS.m, $axT.h, $acR
// 110s t11r xxxx xxxx
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
// secondary accumulator $axT (treat them both as signed). Move product
// register before multiplication to accumulator $acR.
// possible mistake in duddie's doc axT.h rather than axS.h
// flags out: --xx xx0x
void DSPEmitter::mulcmv(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
// s64 acc = dsp_get_long_prod();
get_long_prod();
PUSH(64, R(RAX));
// u16 accm = dsp_get_acc_m(sreg);
get_acc_m(sreg, ESI);
// u16 axh = dsp_get_ax_h(treg);
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
// s64 prod = dsp_multiply(accm, axh);
multiply();
// dsp_set_long_prod(prod);
set_long_prod();
// dsp_set_long_acc(rreg, acc);
POP(64, R(RAX));
set_long_acc(rreg);
// Update_SR_Register64(dsp_get_long_acc(rreg));
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64();
}
#else
Default(opc);
#endif
}
// MULCMVZ $acS.m, $axT.h, $acR
// 110s t01r xxxx xxxx
// (fixed possible bug in duddie's description, s->t)
// Multiply mid part of accumulator register $acS.m by high part $axT.h of
// secondary accumulator $axT (treat them both as signed). Move product
// register before multiplication to accumulator $acR, set (round) low part of
// accumulator $acR.l to zero.
// flags out: --xx xx0x
void DSPEmitter::mulcmvz(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 rreg = (opc >> 8) & 0x1;
u8 treg = (opc >> 11) & 0x1;
u8 sreg = (opc >> 12) & 0x1;
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
// s64 acc = dsp_get_long_prod_round_prodl();
get_long_prod_round_prodl();
PUSH(64, R(RAX));
// u16 accm = dsp_get_acc_m(sreg);
get_acc_m(sreg, ESI);
// u16 axh = dsp_get_ax_h(treg);
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
// s64 prod = dsp_multiply(accm, axh);
multiply();
// dsp_set_long_prod(prod);
set_long_prod();
// dsp_set_long_acc(rreg, acc);
POP(64, R(RAX));
set_long_acc(rreg);
// Update_SR_Register64(dsp_get_long_acc(rreg));
if (!(DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_START_OF_INST) || (DSPAnalyzer::code_flags[compilePC] & DSPAnalyzer::CODE_UPDATE_SR))
{
Update_SR_Register64();
}
#else
Default(opc);
#endif
}
//----
// MADDX ax0.S ax1.T
// 1110 00st xxxx xxxx
// Multiply one part of secondary accumulator $ax0 (selected by S) by
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
// signed) and add result to product register.
void DSPEmitter::maddx(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
// s64 prod = dsp_multiply_add(val1, val2);
multiply_add();
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}
// MSUBX $(0x18+S*2), $(0x19+T*2)
// 1110 01st xxxx xxxx
// Multiply one part of secondary accumulator $ax0 (selected by S) by
// one part of secondary accumulator $ax1 (selected by T) (treat them both as
// signed) and subtract result from product register.
void DSPEmitter::msubx(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
// u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
dsp_op_read_reg(DSP_REG_AXL0 + sreg*2, RSI, SIGN);
// u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
dsp_op_read_reg(DSP_REG_AXL1 + treg*2, RDI, SIGN);
// s64 prod = dsp_multiply_sub(val1, val2);
multiply_sub();
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}
// MADDC $acS.m, $axT.h
// 1110 10st xxxx xxxx
// Multiply middle part of accumulator $acS.m by high part of secondary
// accumulator $axT.h (treat them both as signed) and add result to product
// register.
void DSPEmitter::maddc(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
// u16 accm = dsp_get_acc_m(sreg);
get_acc_m(sreg, ESI);
// u16 axh = dsp_get_ax_h(treg);
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
// s64 prod = dsp_multiply_add(accm, axh);
multiply_add();
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}
// MSUBC $acS.m, $axT.h
// 1110 11st xxxx xxxx
// Multiply middle part of accumulator $acS.m by high part of secondary
// accumulator $axT.h (treat them both as signed) and subtract result from
// product register.
void DSPEmitter::msubc(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 treg = (opc >> 8) & 0x1;
u8 sreg = (opc >> 9) & 0x1;
// u16 accm = dsp_get_acc_m(sreg);
get_acc_m(sreg, ESI);
// u16 axh = dsp_get_ax_h(treg);
dsp_op_read_reg(DSP_REG_AXH0+treg, RDI, SIGN);
// s64 prod = dsp_multiply_sub(accm, axh);
multiply_sub();
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}
// MADD $axS.l, $axS.h
// 1111 001s xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed) and add
// result to product register.
void DSPEmitter::madd(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 sreg = (opc >> 8) & 0x1;
// u16 axl = dsp_get_ax_l(sreg);
dsp_op_read_reg(DSP_REG_AXL0+sreg, RSI, SIGN);
// u16 axh = dsp_get_ax_h(sreg);
dsp_op_read_reg(DSP_REG_AXH0+sreg, RDI, SIGN);
// s64 prod = dsp_multiply_add(axl, axh);
multiply_add();
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}
// MSUB $axS.l, $axS.h
// 1111 011s xxxx xxxx
// Multiply low part $axS.l of secondary accumulator $axS by high part
// $axS.h of secondary accumulator $axS (treat them both as signed) and
// subtract result from product register.
void DSPEmitter::msub(const UDSPInstruction opc)
{
#ifdef _M_X64
u8 sreg = (opc >> 8) & 0x1;
// u16 axl = dsp_get_ax_l(sreg);
dsp_op_read_reg(DSP_REG_AXL0+sreg, RSI, SIGN);
// u16 axh = dsp_get_ax_h(sreg);
dsp_op_read_reg(DSP_REG_AXH0+sreg, RDI, SIGN);
// s64 prod = dsp_multiply_sub(axl, axh);
multiply_sub();
// dsp_set_long_prod(prod);
set_long_prod();
#else
Default(opc);
#endif
}

View File

@ -0,0 +1,540 @@
// Copyright (C) 2011 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "DSPJitRegCache.h"
#include "../DSPEmitter.h"
#include "../DSPMemoryMap.h"
using namespace Gen;
static u16 *reg_ptr(int reg) {
switch(reg) {
case DSP_REG_AR0:
case DSP_REG_AR1:
case DSP_REG_AR2:
case DSP_REG_AR3:
return &g_dsp.r.ar[reg - DSP_REG_AR0];
case DSP_REG_IX0:
case DSP_REG_IX1:
case DSP_REG_IX2:
case DSP_REG_IX3:
return &g_dsp.r.ix[reg - DSP_REG_IX0];
case DSP_REG_WR0:
case DSP_REG_WR1:
case DSP_REG_WR2:
case DSP_REG_WR3:
return &g_dsp.r.wr[reg - DSP_REG_WR0];
case DSP_REG_ST0:
case DSP_REG_ST1:
case DSP_REG_ST2:
case DSP_REG_ST3:
return &g_dsp.r.st[reg - DSP_REG_ST0];
case DSP_REG_ACH0:
case DSP_REG_ACH1:
return &g_dsp.r.ac[reg - DSP_REG_ACH0].h;
case DSP_REG_CR: return &g_dsp.r.cr;
case DSP_REG_SR: return &g_dsp.r.sr;
case DSP_REG_PRODL: return &g_dsp.r.prod.l;
case DSP_REG_PRODM: return &g_dsp.r.prod.m;
case DSP_REG_PRODH: return &g_dsp.r.prod.h;
case DSP_REG_PRODM2: return &g_dsp.r.prod.m2;
case DSP_REG_AXL0:
case DSP_REG_AXL1:
return &g_dsp.r.ax[reg - DSP_REG_AXL0].l;
case DSP_REG_AXH0:
case DSP_REG_AXH1:
return &g_dsp.r.ax[reg - DSP_REG_AXH0].h;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
return &g_dsp.r.ac[reg - DSP_REG_ACL0].l;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
return &g_dsp.r.ac[reg - DSP_REG_ACM0].m;
default:
_assert_msg_(DSPLLE, 0, "cannot happen");
return NULL;
}
}
#define ROTATED_REG_ACCS
//#undef ROTATED_REG_ACCS
DSPJitRegCache::DSPJitRegCache(DSPEmitter &_emitter)
: emitter(_emitter), temporary(false), merged(false) {
for(unsigned int i = 0; i < NUMXREGS; i++) {
xregs[i].guest_reg = DSP_REG_STATIC;
}
xregs[RSP].guest_reg = DSP_REG_STATIC;//stack pointer
xregs[RBX].guest_reg = DSP_REG_STATIC;//extended op backing store
xregs[RBP].guest_reg = DSP_REG_NONE;//definitely usable in dsplle because
//all external calls are protected
#ifdef _M_X64
xregs[R8].guest_reg = DSP_REG_STATIC;//acc0
xregs[R9].guest_reg = DSP_REG_STATIC;//acc1
xregs[R10].guest_reg = DSP_REG_NONE;
xregs[R11].guest_reg = DSP_REG_STATIC;//&g_dsp.r
xregs[R12].guest_reg = DSP_REG_STATIC;//used for cycle counting
xregs[R13].guest_reg = DSP_REG_NONE;
xregs[R14].guest_reg = DSP_REG_NONE;
xregs[R15].guest_reg = DSP_REG_NONE;
#endif
#ifdef _M_X64
acc[0].host_reg = R8;
acc[0].shift = 0;
acc[0].dirty = false;
acc[0].used = false;
acc[0].tmp_reg = INVALID_REG;
acc[1].host_reg = R9;
acc[1].shift = 0;
acc[1].dirty = false;
acc[1].used = false;
acc[1].tmp_reg = INVALID_REG;
#endif
for(unsigned int i = 0; i < 32; i++) {
regs[i].mem = reg_ptr(i);
regs[i].size = 2;
}
#ifdef _M_X64
regs[DSP_REG_ACC0_64].mem = &g_dsp.r.ac[0].val;
regs[DSP_REG_ACC0_64].size = 8;
regs[DSP_REG_ACC1_64].mem = &g_dsp.r.ac[1].val;
regs[DSP_REG_ACC1_64].size = 8;
regs[DSP_REG_PROD_64].mem = &g_dsp.r.prod.val;
regs[DSP_REG_PROD_64].size = 8;
#endif
regs[DSP_REG_AX0_32].mem = &g_dsp.r.ax[0].val;
regs[DSP_REG_AX0_32].size = 4;
regs[DSP_REG_AX1_32].mem = &g_dsp.r.ax[1].val;
regs[DSP_REG_AX1_32].size = 4;
for(unsigned int i = 0; i < DSP_REG_MAX_MEM_BACKED+1; i++) {
regs[i].dirty = false;
#ifdef _M_IX86 // All32
regs[i].loc = M(regs[i].mem);
#else
regs[i].loc = MDisp(R11, PtrOffset(regs[i].mem, &g_dsp.r));
#endif
}
}
DSPJitRegCache::DSPJitRegCache(const DSPJitRegCache &cache)
: emitter(cache.emitter), temporary(true), merged(false)
{
memcpy(xregs,cache.xregs,sizeof(xregs));
#ifdef _M_X64
memcpy(acc,cache.acc,sizeof(acc));
#endif
memcpy(regs,cache.regs,sizeof(regs));
}
DSPJitRegCache& DSPJitRegCache::operator=(const DSPJitRegCache &cache)
{
_assert_msg_(DSPLLE, &emitter == &cache.emitter, "emitter does not match");
_assert_msg_(DSPLLE, temporary, "register cache not temporary??");
merged = false;
memcpy(xregs,cache.xregs,sizeof(xregs));
#ifdef _M_X64
memcpy(acc,cache.acc,sizeof(acc));
#endif
memcpy(regs,cache.regs,sizeof(regs));
return *this;
}
DSPJitRegCache::~DSPJitRegCache()
{
_assert_msg_(DSPLLE, !temporary || merged, "temporary cache not merged");
}
void DSPJitRegCache::flushRegs(DSPJitRegCache &cache, bool emit)
{
cache.merged = true;
#ifdef _M_X64
for(unsigned int i = 0; i < 2; i++) {
if (acc[i].shift > cache.acc[i].shift) {
if (emit)
emitter.ROL(64, R(acc[i].host_reg),
Imm8(acc[i].shift-cache.acc[i].shift));
acc[i].shift = cache.acc[i].shift;
}
if (acc[i].shift < cache.acc[i].shift) {
if (emit)
emitter.ROR(64, R(acc[i].host_reg),
Imm8(cache.acc[i].shift-acc[i].shift));
acc[i].shift = cache.acc[i].shift;
}
}
#endif
}
void DSPJitRegCache::drop()
{
merged = true;
}
void DSPJitRegCache::flushRegs()
{
//also needs to undo any dynamic changes to static allocated regs
//this should have the same effect as
//merge(DSPJitRegCache(emitter));
#ifdef _M_X64
#ifdef ROTATED_REG_ACCS
for(unsigned int i = 0; i < 2; i++) {
if (acc[i].shift > 0) {
emitter.ROL(64, R(acc[i].host_reg),
Imm8(acc[i].shift));
acc[i].shift = 0;
}
_assert_msg_(DSPLLE, !acc[i].used,
"accumulator still in use");
if (acc[i].used)
emitter.INT3();
}
#endif
#endif
}
static u64 ebp_store;
void DSPJitRegCache::loadStaticRegs()
{
#ifdef _M_X64
#ifdef ROTATED_REG_ACCS
emitter.MOV(64, R(R8), MDisp(R11, STRUCT_OFFSET(g_dsp.r, ac[0].val)));
emitter.MOV(64, R(R9), MDisp(R11, STRUCT_OFFSET(g_dsp.r, ac[1].val)));
#endif
emitter.MOV(64, MDisp(R11, PtrOffset(&ebp_store, &g_dsp.r)), R(RBP));
#else
emitter.MOV(32, M(&ebp_store), R(EBP));
#endif
}
void DSPJitRegCache::saveStaticRegs()
{
flushRegs();
#ifdef _M_X64
#ifdef ROTATED_REG_ACCS
emitter.MOV(64, MDisp(R11, STRUCT_OFFSET(g_dsp.r, ac[0].val)), R(R8));
emitter.MOV(64, MDisp(R11, STRUCT_OFFSET(g_dsp.r, ac[1].val)), R(R9));
#endif
emitter.MOV(64, R(RBP), MDisp(R11, PtrOffset(&ebp_store, &g_dsp.r)));
#else
emitter.MOV(32, R(EBP), M(&ebp_store));
#endif
}
void DSPJitRegCache::getReg(int reg, OpArg &oparg, bool load)
{
switch(reg) {
#ifdef _M_X64
#ifdef ROTATED_REG_ACCS
case DSP_REG_ACH0:
case DSP_REG_ACH1:
{
_assert_msg_(DSPLLE, !acc[reg-DSP_REG_ACH0].used,
"accumulator already in use");
if (acc[reg-DSP_REG_ACH0].used)
emitter.INT3();
oparg = R(acc[reg-DSP_REG_ACH0].host_reg);
if (acc[reg-DSP_REG_ACH0].shift < 32) {
emitter.ROR(64, oparg, Imm8(32-acc[reg-DSP_REG_ACH0].shift));
acc[reg-DSP_REG_ACH0].shift = 32;
}
acc[reg-DSP_REG_ACH0].used = true;
}
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
{
_assert_msg_(DSPLLE, !acc[reg-DSP_REG_ACM0].used,
"accumulator already in use");
if (acc[reg-DSP_REG_ACM0].used)
emitter.INT3();
oparg = R(acc[reg-DSP_REG_ACM0].host_reg);
if (acc[reg-DSP_REG_ACM0].shift < 16) {
emitter.ROR(64, oparg, Imm8(16-acc[reg-DSP_REG_ACM0].shift));
acc[reg-DSP_REG_ACM0].shift = 16;
}
if (acc[reg-DSP_REG_ACM0].shift > 16) {
emitter.ROL(64, oparg, Imm8(acc[reg-DSP_REG_ACM0].shift-16));
acc[reg-DSP_REG_ACM0].shift = 16;
}
acc[reg-DSP_REG_ACM0].used = true;
}
break;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
{
_assert_msg_(DSPLLE, !acc[reg-DSP_REG_ACL0].used,
"accumulator already in use");
if (acc[reg-DSP_REG_ACL0].used)
emitter.INT3();
oparg = R(acc[reg-DSP_REG_ACL0].host_reg);
if (acc[reg-DSP_REG_ACL0].shift > 0) {
emitter.ROL(64, oparg, Imm8(acc[reg-DSP_REG_ACL0].shift));
acc[reg-DSP_REG_ACL0].shift = 0;
}
acc[reg-DSP_REG_ACL0].used = true;
}
break;
case DSP_REG_ACC0_64:
case DSP_REG_ACC1_64:
{
if (acc[reg-DSP_REG_ACC0_64].used)
emitter.INT3();
_assert_msg_(DSPLLE, !acc[reg-DSP_REG_ACC0_64].used,
"accumulator already in use");
oparg = R(acc[reg-DSP_REG_ACC0_64].host_reg);
if (load) {
if (acc[reg-DSP_REG_ACC0_64].shift > 0) {
emitter.ROL(64, oparg, Imm8(acc[reg-DSP_REG_ACC0_64].shift));
}
emitter.SHL(64, oparg, Imm8(64-40));//sign extend
emitter.SAR(64, oparg, Imm8(64-40));
}
//don't bother to rotate if caller replaces all data
acc[reg-DSP_REG_ACC0_64].shift = 0;
acc[reg-DSP_REG_ACC0_64].used = true;
}
break;
#endif
#endif
default:
{
/*
getFreeXReg(reg[reg].host_reg);
X64Reg tmp = reg[reg].host_reg;
oparg = R(tmp);
if (load) {
u16 *regp = reg_ptr(reg);
#ifdef _M_IX86 // All32
emitter.MOV(16, oparg, M(regp));
#else
emitter.MOV(16, oparg, MDisp(R11, PtrOffset(regp, &g_dsp.r)));
#endif
}
*/
oparg = regs[reg].loc; //when loading/storing from/to mem, need to consider regs[reg].size
}
break;
}
}
void DSPJitRegCache::putReg(int reg, bool dirty)
{
switch(reg) {
#ifdef _M_X64
#ifdef ROTATED_REG_ACCS
case DSP_REG_ACH0:
case DSP_REG_ACH1:
{
if (dirty) {
if (acc[reg-DSP_REG_ACH0].shift > 0) {
emitter.ROL(64, R(acc[reg-DSP_REG_ACH0].host_reg),
Imm8(acc[reg-DSP_REG_ACH0].shift));
acc[reg-DSP_REG_ACH0].shift = 0;
}
emitter.SHL(64, R(acc[reg-DSP_REG_ACH0].host_reg), Imm8(64-40));//sign extend
emitter.SAR(64, R(acc[reg-DSP_REG_ACH0].host_reg), Imm8(64-40));
}
acc[reg-DSP_REG_ACH0].used = false;
}
break;
case DSP_REG_ACM0:
case DSP_REG_ACM1:
{
acc[reg-DSP_REG_ACM0].used = false;
}
break;
case DSP_REG_ACL0:
case DSP_REG_ACL1:
acc[reg-DSP_REG_ACL0].used = false;
break;
case DSP_REG_ACC0_64:
case DSP_REG_ACC1_64:
{
if (dirty) {
OpArg _reg = R(acc[reg-DSP_REG_ACC0_64].host_reg);
emitter.SHL(64, _reg, Imm8(64-40));//sign extend
emitter.SAR(64, _reg, Imm8(64-40));
}
acc[reg-DSP_REG_ACC0_64].used = false;
}
break;
#else
case DSP_REG_ACH0:
case DSP_REG_ACH1:
{
//need to fix in memory for now.
u16 *regp = reg_ptr(reg);
OpArg mem;
mem = MDisp(R11,PtrOffset(regp,&g_dsp.r));
X64Reg tmp;
getFreeXReg(tmp);
// sign extend from the bottom 8 bits.
emitter.MOVSX(16, 8, tmp, mem);
emitter.MOV(16, mem, R(tmp));
putXReg(tmp);
}
break;
#endif
#else
case DSP_REG_ACH0:
case DSP_REG_ACH1:
{
//need to fix in memory for now.
u16 *regp = reg_ptr(reg);
OpArg mem;
mem = M(regp);
X64Reg tmp;
getFreeXReg(tmp);
// sign extend from the bottom 8 bits.
emitter.MOVSX(16, 8, tmp, mem);
emitter.MOV(16, mem, R(tmp));
putXReg(tmp);
}
break;
#endif
default:
{
/*
X64Reg tmp = reg[reg].host_reg;
if(dirty) {
u16 *regp = reg_ptr(reg);
#ifdef _M_IX86 // All32
emitter.MOV(16, M(dregp), R(tmp));
#else
emitter.MOV(16, MDisp(R11, PtrOffset(dregp, &g_dsp.r)), R(tmp));
#endif
}
*/
}
break;
}
}
void DSPJitRegCache::readReg(int sreg, X64Reg host_dreg, DSPJitSignExtend extend)
{
OpArg reg;
getReg(sreg, reg);
switch(regs[sreg].size) {
case 2:
switch(extend) {
#ifdef _M_X64
case SIGN: emitter.MOVSX(64, 16, host_dreg, reg); break;
case ZERO: emitter.MOVZX(64, 16, host_dreg, reg); break;
#else
case SIGN: emitter.MOVSX(32, 16, host_dreg, reg); break;
case ZERO: emitter.MOVZX(32, 16, host_dreg, reg); break;
#endif
case NONE: emitter.MOV(16, R(host_dreg), reg); break;
}
break;
case 4:
#ifdef _M_X64
switch(extend) {
case SIGN: emitter.MOVSX(64, 32, host_dreg, reg); break;
case ZERO: emitter.MOVZX(64, 32, host_dreg, reg); break;
case NONE: emitter.MOV(32, R(host_dreg), reg); break;
}
#else
emitter.MOV(32, R(host_dreg), reg); break;
#endif
break;
#ifdef _M_X64
case 8:
emitter.MOV(64, R(host_dreg), reg); break;
break;
#endif
default:
_assert_msg_(DSPLLE, 0, "unsupported memory size");
break;
}
putReg(sreg, false);
}
void DSPJitRegCache::writeReg(int dreg, OpArg arg)
{
OpArg reg;
getReg(dreg, reg, false);
switch(regs[dreg].size) {
case 2: emitter.MOV(16, reg, arg); break;
case 4: emitter.MOV(32, reg, arg); break;
#ifdef _M_X64
case 8: emitter.MOV(64, reg, arg); break;
#endif
default:
_assert_msg_(DSPLLE, 0, "unsupported memory size");
break;
}
putReg(dreg, true);
}
X64Reg DSPJitRegCache::spillXReg()
{
//todo: implement
return INVALID_REG;
}
void DSPJitRegCache::spillXReg(X64Reg reg)
{
//todo: implement
}
X64Reg DSPJitRegCache::findFreeXReg()
{
int i;
for(i = 0; i < NUMXREGS; i++) {
if (xregs[i].guest_reg == DSP_REG_NONE) {
return (X64Reg)i;
}
}
return INVALID_REG;
}
void DSPJitRegCache::getFreeXReg(X64Reg &reg)
{
reg = findFreeXReg();
if (reg == INVALID_REG)
reg = spillXReg();
_assert_msg_(DSPLLE, reg != INVALID_REG, "could not find register");
xregs[reg].guest_reg = DSP_REG_USED;
}
void DSPJitRegCache::getXReg(X64Reg reg)
{
if (xregs[reg].guest_reg != DSP_REG_NONE)
spillXReg(reg);
_assert_msg_(DSPLLE, xregs[reg].guest_reg != DSP_REG_NONE, "register already in use");
xregs[reg].guest_reg = DSP_REG_USED;
}
void DSPJitRegCache::putXReg(X64Reg reg)
{
_assert_msg_(DSPLLE, xregs[reg].guest_reg == DSP_REG_USED,
"putXReg without get(Free)XReg");
xregs[reg].guest_reg = DSP_REG_NONE;
}

View File

@ -0,0 +1,169 @@
// Copyright (C) 2011 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _DSPJITREGCACHE_H
#define _DSPJITREGCACHE_H
#include "x64Emitter.h"
class DSPEmitter;
enum DSPJitRegSpecial {
DSP_REG_ACC0_64 =32,
DSP_REG_ACC1_64 =33,
DSP_REG_AX0_32 =34,
DSP_REG_AX1_32 =35,
DSP_REG_PROD_64 =36,
DSP_REG_MAX_MEM_BACKED = 36,
DSP_REG_USED =253,
DSP_REG_STATIC =254,
DSP_REG_NONE =255
};
enum DSPJitSignExtend {
SIGN, ZERO, NONE
};
#ifdef _M_X64
#define NUMXREGS 16
#elif _M_IX86
#define NUMXREGS 8
#endif
class DSPJitRegCache {
private:
struct X64CachedReg
{
int guest_reg; //including DSPJitRegSpecial
};
struct DynamicReg {
Gen::OpArg loc;
void *mem;
size_t size;
bool dirty;
};
#ifdef _M_X64
//when there is a way to do this efficiently in x86, uncondition
struct {
Gen::X64Reg host_reg;
int shift;
bool dirty;
bool used;
Gen::X64Reg tmp_reg;
} acc[2];
#endif
DynamicReg regs[DSP_REG_MAX_MEM_BACKED+1];
X64CachedReg xregs[NUMXREGS];
DSPEmitter &emitter;
bool temporary;
bool merged;
private:
//find a free host reg
Gen::X64Reg findFreeXReg();
Gen::X64Reg spillXReg();
void spillXReg(Gen::X64Reg reg);
public:
DSPJitRegCache(DSPEmitter &_emitter);
//for branching into multiple control flows
DSPJitRegCache(const DSPJitRegCache &cache);
DSPJitRegCache& operator=(const DSPJitRegCache &cache);
~DSPJitRegCache();
//merge must be done _before_ leaving the code branch, so we can fix
//up any differences in state
void flushRegs(DSPJitRegCache &cache, bool emit = true);
/* since some use cases are non-trivial, some examples:
//this does not modify the final state of gpr
<code using gpr>
FixupBranch b = JCC();
DSPJitRegCache c = gpr;
<code using c>
gpr.flushRegs(c);
SetBranchTarget(b);
<code using gpr>
//this does not modify the final state of gpr
<code using gpr>
DSPJitRegCache c = gpr;
FixupBranch b1 = JCC();
<code using gpr>
gpr.flushRegs(c);
FixupBranch b2 = JMP();
SetBranchTarget(b1);
<code using gpr>
gpr.flushRegs(c);
SetBranchTarget(b2);
<code using gpr>
//this allows gpr to be modified in the second branch
//and fixes gpr according to the results form in the first branch
<code using gpr>
DSPJitRegCache c = gpr;
FixupBranch b1 = JCC();
<code using c>
FixupBranch b2 = JMP();
SetBranchTarget(b1);
<code using gpr>
gpr.flushRegs(c);
SetBranchTarget(b2);
<code using gpr>
//this does not modify the final state of gpr
<code using gpr>
u8* b = GetCodePtr();
DSPJitRegCache c = gpr;
<code using gpr>
gpr.flushRegs(c);
JCC(b);
<code using gpr>
this all is not needed when gpr would not be used at all in the
conditional branch
*/
//drop this copy without warning
void drop();
//prepare state so that another flushed DSPJitRegCache can take over
void flushRegs();
void loadStaticRegs();//load statically allocated regs from memory
void saveStaticRegs();//save statically allocated regs to memory
//gives no SCALE_RIP with abs(offset) >= 0x80000000
void getReg(int reg, Gen::OpArg &oparg, bool load = true);
//done with all usages of OpArg above
void putReg(int reg, bool dirty = true);
void readReg(int sreg, Gen::X64Reg host_dreg, DSPJitSignExtend extend);
void writeReg(int dreg, Gen::OpArg arg);
//find a free host reg, spill if used, reserve
void getFreeXReg(Gen::X64Reg &reg);
//spill a specific host reg if used, reserve
void getXReg(Gen::X64Reg reg);
//unreserve the given host reg
void putXReg(Gen::X64Reg reg);
};
#endif /*_DSPJITREGCACHE_H*/

View File

@ -0,0 +1,596 @@
// Copyright (C) 2010 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "../DSPMemoryMap.h"
#include "../DSPHWInterface.h"
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
using namespace Gen;
// addr math
//
// These functions detect overflow by checking if
// the bit past the top of the mask(WR) has changed in AR.
// They detect values under the minimum for a mask by adding wr + 1
// and checking if the bit past the top of the mask doesn't change.
// Both are done while ignoring changes due to values/holes in IX
// above the mask.
// EAX = g_dsp.r.ar[reg]
// EDX = g_dsp.r.wr[reg]
// EDI = temp
// ECX = temp
void DSPEmitter::increment_addr_reg(int reg)
{
OpArg ar_reg;
OpArg wr_reg;
gpr.getReg(DSP_REG_WR0+reg,wr_reg);
MOVZX(32, 16, EDX, wr_reg);
gpr.putReg(DSP_REG_WR0+reg);
gpr.getReg(DSP_REG_AR0+reg,ar_reg);
MOVZX(32, 16, EAX, ar_reg);
//u32 nar = ar + 1;
MOV(32, R(EDI), R(EAX));
ADD(32, R(EAX), Imm8(1));
// if ((nar ^ ar) > ((wr | 1) << 1))
// nar -= wr + 1;
XOR(32, R(EDI), R(EAX));
LEA(32, ECX, MComplex(EDX, EDX, 1, 0));
OR(32, R(ECX), Imm8(2));
CMP(32, R(EDI), R(ECX));
FixupBranch nowrap = J_CC(CC_BE);
SUB(16, R(AX), R(DX));
SUB(16, R(AX), Imm8(1));
SetJumpTarget(nowrap);
// g_dsp.r.ar[reg] = nar;
MOV(16, ar_reg, R(AX));
gpr.putReg(DSP_REG_AR0+reg);
}
// EAX = g_dsp.r.ar[reg]
// EDX = g_dsp.r.wr[reg]
// EDI = temp
// ECX = temp
void DSPEmitter::decrement_addr_reg(int reg)
{
OpArg ar_reg;
OpArg wr_reg;
gpr.getReg(DSP_REG_WR0+reg,wr_reg);
MOVZX(32, 16, EDX, wr_reg);
gpr.putReg(DSP_REG_WR0+reg);
gpr.getReg(DSP_REG_AR0+reg,ar_reg);
MOVZX(32, 16, EAX, ar_reg);
// u32 nar = ar + wr;
// edi = nar
LEA(32, EDI, MComplex(EAX, EDX, 1, 0));
// if (((nar ^ ar) & ((wr | 1) << 1)) > wr)
// nar -= wr + 1;
XOR(32, R(EAX), R(EDI));
LEA(32, ECX, MComplex(EDX, EDX, 1, 0));
OR(32, R(ECX), Imm8(2));
AND(32, R(EAX), R(ECX));
CMP(32, R(EAX), R(EDX));
FixupBranch nowrap = J_CC(CC_BE);
SUB(16, R(DI), R(DX));
SUB(16, R(DI), Imm8(1));
SetJumpTarget(nowrap);
// g_dsp.r.ar[reg] = nar;
MOV(16, ar_reg, R(DI));
gpr.putReg(DSP_REG_AR0+reg);
}
// Increase addr register according to the correspond ix register
// EAX = g_dsp.r.ar[reg]
// EDX = g_dsp.r.wr[reg]
// ESI = g_dsp.r.ix[reg]
// ECX = temp
// EDI = temp
void DSPEmitter::increase_addr_reg(int reg)
{
OpArg ar_reg;
OpArg wr_reg;
OpArg ix_reg;
gpr.getReg(DSP_REG_WR0+reg,wr_reg);
gpr.getReg(DSP_REG_IX0+reg,ix_reg);
MOVZX(32, 16, EDX, wr_reg);
MOVSX(32, 16, ESI, ix_reg);
gpr.putReg(DSP_REG_WR0+reg);
gpr.putReg(DSP_REG_IX0+reg);
gpr.getReg(DSP_REG_AR0+reg,ar_reg);
MOVZX(32, 16, EAX, ar_reg);
//u32 nar = ar + ix;
//edi = nar
LEA(32, EDI, MComplex(EAX, ESI, 1, 0));
//u32 dar = (nar ^ ar ^ ix) & ((wr | 1) << 1);
//eax = dar
XOR(32, R(EAX), R(ESI));
XOR(32, R(EAX), R(EDI));
LEA(32, ECX, MComplex(EDX, EDX, 1, 0));
OR(32, R(ECX), Imm8(2));
AND(32, R(EAX), R(ECX));
//if (ix >= 0)
TEST(32, R(ESI), R(ESI));
FixupBranch negative = J_CC(CC_S);
//if (dar > wr)
CMP(32, R(EAX), R(EDX));
FixupBranch done = J_CC(CC_BE);
//nar -= wr + 1;
SUB(16, R(DI), R(DX));
SUB(16, R(DI), Imm8(1));
FixupBranch done2 = J();
//else
SetJumpTarget(negative);
//if ((((nar + wr + 1) ^ nar) & dar) <= wr)
LEA(32, ECX, MComplex(EDI, EDX, 1, 1));
XOR(32, R(ECX), R(EDI));
AND(32, R(ECX), R(EAX));
CMP(32, R(ECX), R(EDX));
FixupBranch done3 = J_CC(CC_A);
//nar += wr + 1;
LEA(32, EDI, MComplex(EDI, EDX, 1, 1));
SetJumpTarget(done);
SetJumpTarget(done2);
SetJumpTarget(done3);
// g_dsp.r.ar[reg] = nar;
MOV(16, ar_reg, R(DI));
gpr.putReg(DSP_REG_AR0+reg);
}
// Decrease addr register according to the correspond ix register
// EAX = g_dsp.r.ar[reg]
// EDX = g_dsp.r.wr[reg]
// ESI = g_dsp.r.ix[reg]
// ECX = temp
// EDI = temp
void DSPEmitter::decrease_addr_reg(int reg)
{
OpArg ar_reg;
OpArg wr_reg;
OpArg ix_reg;
gpr.getReg(DSP_REG_WR0+reg,wr_reg);
gpr.getReg(DSP_REG_IX0+reg,ix_reg);
MOVZX(32, 16, EDX, wr_reg);
MOVSX(32, 16, ESI, ix_reg);
gpr.putReg(DSP_REG_WR0+reg);
gpr.putReg(DSP_REG_IX0+reg);
gpr.getReg(DSP_REG_AR0+reg,ar_reg);
MOVZX(32, 16, EAX, ar_reg);
NOT(32, R(ESI)); //esi = ~ix
//u32 nar = ar - ix; (ar + ~ix + 1)
LEA(32, EDI, MComplex(EAX, ESI, 1, 1));
//u32 dar = (nar ^ ar ^ ~ix) & ((wr | 1) << 1);
//eax = dar
XOR(32, R(EAX), R(ESI));
XOR(32, R(EAX), R(EDI));
LEA(32, ECX, MComplex(EDX, EDX, 1, 0));
OR(32, R(ECX), Imm8(2));
AND(32, R(EAX), R(ECX));
//if ((u32)ix > 0xFFFF8000) ==> (~ix < 0x00007FFF)
CMP(32, R(ESI), Imm32(0x00007FFF));
FixupBranch positive = J_CC(CC_AE);
//if (dar > wr)
CMP(32, R(EAX), R(EDX));
FixupBranch done = J_CC(CC_BE);
//nar -= wr + 1;
SUB(16, R(DI), R(DX));
SUB(16, R(DI), Imm8(1));
FixupBranch done2 = J();
//else
SetJumpTarget(positive);
//if ((((nar + wr + 1) ^ nar) & dar) <= wr)
LEA(32, ECX, MComplex(EDI, EDX, 1, 1));
XOR(32, R(ECX), R(EDI));
AND(32, R(ECX), R(EAX));
CMP(32, R(ECX), R(EDX));
FixupBranch done3 = J_CC(CC_A);
//nar += wr + 1;
LEA(32, EDI, MComplex(EDI, EDX, 1, 1));
SetJumpTarget(done);
SetJumpTarget(done2);
SetJumpTarget(done3);
//return nar
MOV(16, ar_reg, R(DI));
gpr.putReg(DSP_REG_AR0+reg);
}
// EAX - destination address
// ECX - value
// ESI - Base of dram
void DSPEmitter::dmem_write()
{
// if (saddr == 0)
CMP(16, R(EAX), Imm16(0x0fff));
FixupBranch ifx = J_CC(CC_A);
// g_dsp.dram[addr & DSP_DRAM_MASK] = val;
AND(16, R(EAX), Imm16(DSP_DRAM_MASK));
#ifdef _M_X64
MOV(64, R(ESI), ImmPtr(g_dsp.dram));
#else
MOV(32, R(ESI), ImmPtr(g_dsp.dram));
#endif
MOV(16, MComplex(ESI, EAX, 2, 0), R(ECX));
FixupBranch end = J();
// else if (saddr == 0xf)
SetJumpTarget(ifx);
// Does it mean gdsp_ifx_write needs u32 rather than u16?
DSPJitRegCache c(gpr);
SaveDSPRegs();
ABI_CallFunctionRR((void *)gdsp_ifx_write, EAX, ECX);
LoadDSPRegs();
gpr.flushRegs(c);
SetJumpTarget(end);
}
// ECX - value
void DSPEmitter::dmem_write_imm(u16 address)
{
switch (address >> 12)
{
case 0x0: // 0xxx DRAM
#ifdef _M_IX86 // All32
MOV(16, M(&g_dsp.dram[address & DSP_DRAM_MASK]), R(ECX));
#else
MOV(64, R(RDX), ImmPtr(g_dsp.dram));
MOV(16, MDisp(RDX, (address & DSP_DRAM_MASK)*2), R(ECX));
#endif
break;
case 0xf: // Fxxx HW regs
MOV(16, R(EAX), Imm16(address));
SaveDSPRegs();
ABI_CallFunctionRR((void *)gdsp_ifx_write, EAX, ECX);
LoadDSPRegs();
break;
default: // Unmapped/non-existing memory
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Write to UNKNOWN (%04x) memory",
g_dsp.pc, address);
break;
}
}
// In: ECX - the address to read
// Out: EAX - the result of the read (used by caller)
// ESI - Base
void DSPEmitter::imem_read()
{
// if (addr == 0)
CMP(16, R(ECX), Imm16(0x0fff));
FixupBranch irom = J_CC(CC_A);
// return g_dsp.iram[addr & DSP_IRAM_MASK];
AND(16, R(ECX), Imm16(DSP_IRAM_MASK));
#ifdef _M_X64
MOV(64, R(ESI), ImmPtr(g_dsp.iram));
#else
MOV(32, R(ESI), ImmPtr(g_dsp.iram));
#endif
MOV(16, R(EAX), MComplex(ESI, ECX, 2, 0));
FixupBranch end = J();
SetJumpTarget(irom);
// else if (addr == 0x8)
// return g_dsp.irom[addr & DSP_IROM_MASK];
AND(16, R(ECX), Imm16(DSP_IROM_MASK));
#ifdef _M_X64
MOV(64, R(ESI), ImmPtr(g_dsp.irom));
#else
MOV(32, R(ESI), ImmPtr(g_dsp.irom));
#endif
MOV(16, R(EAX), MComplex(ESI, ECX, 2, 0));
SetJumpTarget(end);
}
// In: ECX - the address to read
// Out: EAX - the result of the read (used by caller)
// ESI - Base
// Trashes R11 on gdsp_ifx_read
void DSPEmitter::dmem_read()
{
// if (saddr == 0)
CMP(16, R(ECX), Imm16(0x0fff));
FixupBranch dram = J_CC(CC_A);
// return g_dsp.dram[addr & DSP_DRAM_MASK];
#ifdef _M_X64
AND(16, R(ECX), Imm16(DSP_DRAM_MASK));
MOVZX(64, 16, RCX, R(RCX));
MOV(64, R(ESI), ImmPtr(g_dsp.dram));
#else
AND(32, R(ECX), Imm32(DSP_DRAM_MASK));
MOV(32, R(ESI), ImmPtr(g_dsp.dram));
#endif
MOV(16, R(EAX), MComplex(ESI, ECX, 2, 0));
FixupBranch end = J();
SetJumpTarget(dram);
// else if (saddr == 0x1)
CMP(16, R(ECX), Imm16(0x1fff));
FixupBranch ifx = J_CC(CC_A);
// return g_dsp.coef[addr & DSP_COEF_MASK];
#ifdef _M_X64
AND(16, R(ECX), Imm16(DSP_COEF_MASK));
MOVZX(64, 16, RCX, R(RCX));
MOV(64, R(ESI), ImmPtr(g_dsp.coef));
#else
AND(32, R(ECX), Imm32(DSP_COEF_MASK));
MOV(32, R(ESI), ImmPtr(g_dsp.coef));
#endif
MOV(16, R(EAX), MComplex(ESI, ECX, 2, 0));
FixupBranch end2 = J();
SetJumpTarget(ifx);
// else if (saddr == 0xf)
// return gdsp_ifx_read(addr);
DSPJitRegCache c(gpr);
SaveDSPRegs();
ABI_CallFunctionR((void *)gdsp_ifx_read, ECX);
LoadDSPRegs();
gpr.flushRegs(c);
SetJumpTarget(end);
SetJumpTarget(end2);
}
void DSPEmitter::dmem_read_imm(u16 address)
{
switch (address >> 12)
{
case 0x0: // 0xxx DRAM
#ifdef _M_IX86 // All32
MOV(16, R(EAX), M(&g_dsp.dram[address & DSP_DRAM_MASK]));
#else
MOV(64, R(RDX), ImmPtr(g_dsp.dram));
MOV(16, R(EAX), MDisp(RDX, (address & DSP_DRAM_MASK)*2));
#endif
break;
case 0x1: // 1xxx COEF
#ifdef _M_IX86 // All32
MOV(16, R(EAX), Imm16(g_dsp.coef[address & DSP_COEF_MASK]));
#else
MOV(64, R(RDX), ImmPtr(g_dsp.coef));
MOV(16, R(EAX), MDisp(RDX, (address & DSP_COEF_MASK)*2));
#endif
break;
case 0xf: // Fxxx HW regs
SaveDSPRegs();
ABI_CallFunctionC16((void *)gdsp_ifx_read, address);
LoadDSPRegs();
break;
default: // Unmapped/non-existing memory
ERROR_LOG(DSPLLE, "%04x DSP ERROR: Read from UNKNOWN (%04x) memory",
g_dsp.pc, address);
}
}
// Returns s64 in RAX
void DSPEmitter::get_long_prod(X64Reg long_prod)
{
#ifdef _M_X64
// MOV(64, R(R11), ImmPtr(&g_dsp.r));
//s64 val = (s8)(u8)g_dsp.r[DSP_REG_PRODH];
OpArg reg;
gpr.getReg(DSP_REG_PROD_64, reg);
X64Reg tmp;
gpr.getFreeXReg(tmp);
MOV(64, R(long_prod), reg);
MOV(64, R(tmp), R(long_prod));
SHL(64, R(long_prod), Imm8(64-40));//sign extend
SAR(64, R(long_prod), Imm8(64-40));
SHR(64, R(tmp), Imm8(48));
SHL(64, R(tmp), Imm8(16));
ADD(64, R(long_prod), R(tmp));
gpr.putXReg(tmp);
gpr.putReg(DSP_REG_PROD_64, false);
#endif
}
// Returns s64 in RAX
// Clobbers RSI
void DSPEmitter::get_long_prod_round_prodl(X64Reg long_prod)
{
#ifdef _M_X64
//s64 prod = dsp_get_long_prod();
get_long_prod(long_prod);
//if (prod & 0x10000) prod = (prod + 0x8000) & ~0xffff;
TEST(32, R(long_prod), Imm32(0x10000));
FixupBranch jump = J_CC(CC_Z);
ADD(64, R(long_prod), Imm32(0x8000));
MOV(64, R(ESI), Imm64(~0xffff));
AND(64, R(long_prod), R(RSI));
FixupBranch _ret = J();
//else prod = (prod + 0x7fff) & ~0xffff;
SetJumpTarget(jump);
ADD(64, R(long_prod), Imm32(0x7fff));
MOV(64, R(RSI), Imm64(~0xffff));
AND(64, R(long_prod), R(RSI));
SetJumpTarget(_ret);
//return prod;
#endif
}
// For accurate emulation, this is wrong - but the real prod registers behave
// in completely bizarre ways. Probably not meaningful to emulate them accurately.
// In: RAX = s64 val
void DSPEmitter::set_long_prod()
{
#ifdef _M_X64
OpArg reg;
gpr.getReg(DSP_REG_PROD_64, reg, false);
X64Reg tmp;
gpr.getFreeXReg(tmp);
MOV(64, R(tmp), Imm64(0x000000ffffffffffULL));
AND(64, R(RAX), R(tmp));
// g_dsp.r[DSP_REG_PRODL] = (u16)val;
MOV(64, reg, R(RAX));
gpr.putXReg(tmp);
gpr.putReg(DSP_REG_PROD_64, true);
#endif
}
// Returns s64 in RAX
// Clobbers RSI
void DSPEmitter::round_long_acc(X64Reg long_acc)
{
#ifdef _M_X64
//if (prod & 0x10000) prod = (prod + 0x8000) & ~0xffff;
TEST(32, R(long_acc), Imm32(0x10000));
FixupBranch jump = J_CC(CC_Z);
ADD(64, R(long_acc), Imm32(0x8000));
MOV(64, R(ESI), Imm64(~0xffff));
AND(64, R(long_acc), R(RSI));
FixupBranch _ret = J();
//else prod = (prod + 0x7fff) & ~0xffff;
SetJumpTarget(jump);
ADD(64, R(long_acc), Imm32(0x7fff));
MOV(64, R(RSI), Imm64(~0xffff));
AND(64, R(long_acc), R(RSI));
SetJumpTarget(_ret);
//return prod;
#endif
}
// Returns s64 in acc
void DSPEmitter::get_long_acc(int _reg, X64Reg acc)
{
#ifdef _M_X64
OpArg reg;
gpr.getReg(DSP_REG_ACC0_64+_reg, reg);
MOV(64, R(acc), reg);
gpr.putReg(DSP_REG_ACC0_64+_reg, false);
#endif
}
// In: acc = s64 val
void DSPEmitter::set_long_acc(int _reg, X64Reg acc)
{
#ifdef _M_X64
OpArg reg;
gpr.getReg(DSP_REG_ACC0_64+_reg, reg, false);
MOV(64, reg, R(acc));
gpr.putReg(DSP_REG_ACC0_64+_reg);
#endif
}
// Returns s16 in AX
void DSPEmitter::get_acc_l(int _reg, X64Reg acl, bool sign)
{
// return g_dsp.r[DSP_REG_ACM0 + _reg];
gpr.readReg(_reg+DSP_REG_ACL0, acl, sign?SIGN:ZERO);
}
void DSPEmitter::set_acc_l(int _reg, OpArg arg)
{
// return g_dsp.r[DSP_REG_ACM0 + _reg];
gpr.writeReg(_reg+DSP_REG_ACL0,arg);
}
// Returns s16 in AX
void DSPEmitter::get_acc_m(int _reg, X64Reg acm, bool sign)
{
// return g_dsp.r[DSP_REG_ACM0 + _reg];
gpr.readReg(_reg+DSP_REG_ACM0, acm, sign?SIGN:ZERO);
}
// In: s16 in AX
void DSPEmitter::set_acc_m(int _reg, OpArg arg)
{
// return g_dsp.r.ac[_reg].m;
gpr.writeReg(_reg+DSP_REG_ACM0,arg);
}
// Returns s16 in AX
void DSPEmitter::get_acc_h(int _reg, X64Reg ach, bool sign)
{
// return g_dsp.r.ac[_reg].h;
gpr.readReg(_reg+DSP_REG_ACH0, ach, sign?SIGN:ZERO);
}
// In: s16 in AX
void DSPEmitter::set_acc_h(int _reg, OpArg arg)
{
// return g_dsp.r[DSP_REG_ACM0 + _reg];
gpr.writeReg(_reg+DSP_REG_ACH0,arg);
}
// Returns u32 in EAX
void DSPEmitter::get_long_acx(int _reg, X64Reg acx)
{
// return ((u32)g_dsp.r[DSP_REG_AXH0 + _reg] << 16) | g_dsp.r[DSP_REG_AXL0 + _reg];
gpr.readReg(_reg+DSP_REG_AX0_32, acx, SIGN);
}
// Returns s16 in EAX
void DSPEmitter::get_ax_l(int _reg, X64Reg axl)
{
// return (s16)g_dsp.r[DSP_REG_AXL0 + _reg];
gpr.readReg(_reg+DSP_REG_AXL0, axl, SIGN);
}
// Returns s16 in EAX
void DSPEmitter::get_ax_h(int _reg, X64Reg axh)
{
// return (s16)g_dsp.r[DSP_REG_AXH0 + _reg];
gpr.readReg(_reg+DSP_REG_AXH0, axh, SIGN);
}
void DSPEmitter::LoadDSPRegs()
{
#ifdef _M_X64
MOV(64, R(R11), ImmPtr(&g_dsp.r));
#endif
// Load DSP register state here...
gpr.loadStaticRegs();
}
void DSPEmitter::SaveDSPRegs()
{
// Save DSP register state here...
gpr.saveStaticRegs();
}

View File

@ -0,0 +1,21 @@
// Copyright (C) 2010 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef __DSPJITUTIL_H__
#define __DSPJITUTIL_H__
#endif /*__DSPJITUTIL_H__*/

View File

@ -0,0 +1,85 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "LabelMap.h"
#include "DSPTables.h"
LabelMap::LabelMap()
{
}
void LabelMap::RegisterDefaults()
{
for (int i = 0; i < 0x24; i++)
{
if (regnames[i].name)
RegisterLabel(regnames[i].name, regnames[i].addr);
}
for (int i = 0; i < (int)pdlabels_size; i++)
{
if (pdlabels[i].name)
RegisterLabel(pdlabels[i].name, pdlabels[i].addr);
}
}
void LabelMap::RegisterLabel(const std::string &label, u16 lval, LabelType type)
{
u16 old_value;
if (GetLabelValue(label, &old_value) && old_value != lval)
{
printf("WARNING: Redefined label %s to %04x - old value %04x\n",
label.c_str(), lval, old_value);
DeleteLabel(label);
}
labels.push_back(label_t(label, lval, type));
}
void LabelMap::DeleteLabel(const std::string &label)
{
for (std::vector<label_t>::iterator iter = labels.begin();
iter != labels.end(); ++iter)
{
if (!label.compare(iter->name))
{
labels.erase(iter);
return;
}
}
}
bool LabelMap::GetLabelValue(const std::string &label, u16 *value, LabelType type) const
{
for (u32 i = 0; i < labels.size(); i++)
{
if (!label.compare(labels[i].name))
{
if (type & labels[i].type) {
*value = labels[i].addr;
return true;
} else {
printf("WARNING: Wrong label type requested. %s\n", label.c_str());
}
}
}
return false;
}
void LabelMap::Clear()
{
labels.clear();
}

View File

@ -0,0 +1,55 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _LABELMAP_H
#define _LABELMAP_H
#include <string>
#include <vector>
#include "Common.h"
enum LabelType
{
LABEL_IADDR = 1, // Jump addresses, etc
LABEL_DADDR = 2, // Data addresses, etc
LABEL_VALUE = 4,
LABEL_ANY = 0xFF,
};
class LabelMap
{
struct label_t
{
label_t(const std::string &lbl, s32 address, LabelType ltype) : name(lbl), addr(address), type(ltype) {}
std::string name;
s32 addr;
LabelType type;
};
std::vector<label_t> labels;
public:
LabelMap();
~LabelMap() { }
void RegisterDefaults();
void RegisterLabel(const std::string &label, u16 lval, LabelType type = LABEL_VALUE);
void DeleteLabel(const std::string &label);
bool GetLabelValue(const std::string &label, u16 *value, LabelType type = LABEL_ANY) const;
void Clear();
};
#endif // _LABELMAP_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,139 @@
/*====================================================================
project: GameCube DSP Tool (gcdsp)
created: 2005.03.04
mail: duddie@walla.com
Copyright (c) 2005 Duddie
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.
====================================================================*/
#ifndef _DSP_ASSEMBLE_H
#define _DSP_ASSEMBLE_H
#include <string>
#include <map>
#include "Common.h"
#include "disassemble.h"
#include "DSPTables.h"
#include "LabelMap.h"
enum err_t
{
ERR_OK = 0,
ERR_UNKNOWN,
ERR_UNKNOWN_OPCODE,
ERR_NOT_ENOUGH_PARAMETERS,
ERR_TOO_MANY_PARAMETERS,
ERR_WRONG_PARAMETER,
ERR_EXPECTED_PARAM_STR,
ERR_EXPECTED_PARAM_VAL,
ERR_EXPECTED_PARAM_REG,
ERR_EXPECTED_PARAM_MEM,
ERR_EXPECTED_PARAM_IMM,
ERR_INCORRECT_BIN,
ERR_INCORRECT_HEX,
ERR_INCORRECT_DEC,
ERR_LABEL_EXISTS,
ERR_UNKNOWN_LABEL,
ERR_NO_MATCHING_BRACKETS,
ERR_EXT_CANT_EXTEND_OPCODE,
ERR_EXT_PAR_NOT_EXT,
ERR_WRONG_PARAMETER_ACC,
ERR_WRONG_PARAMETER_MID_ACC,
ERR_INVALID_REGISTER,
ERR_OUT_RANGE_NUMBER
};
// Unless you want labels to carry over between files, you probably
// want to create a new DSPAssembler for every file you assemble.
class DSPAssembler
{
public:
DSPAssembler(const AssemblerSettings &settings);
~DSPAssembler();
// line_numbers is optional (and not yet implemented). It'll receieve a list of ints,
// one for each word of code, indicating the source assembler code line number it came from.
// If returns false, call GetErrorString to get some text to present to the user.
bool Assemble(const char *text, std::vector<u16> &code, std::vector<int> *line_numbers = NULL);
std::string GetErrorString() const { return last_error_str; }
err_t GetError() const { return last_error; }
private:
struct param_t
{
u32 val;
partype_t type;
char *str;
};
enum segment_t
{
SEGMENT_CODE = 0,
SEGMENT_DATA,
SEGMENT_OVERLAY,
SEGMENT_MAX
};
// Utility functions
s32 ParseValue(const char *str);
u32 ParseExpression(const char *ptr);
u32 GetParams(char *parstr, param_t *par);
void InitPass(int pass);
bool AssembleFile(const char *fname, int pass);
void ShowError(err_t err_code, const char *extra_info = NULL);
// void ShowWarning(err_t err_code, const char *extra_info = NULL);
char *FindBrackets(char *src, char *dst);
const opc_t *FindOpcode(const char *opcode, u32 par_count, const opc_t * const opcod, int opcod_size);
bool VerifyParams(const opc_t *opc, param_t *par, int count, bool ext = false);
void BuildCode(const opc_t *opc, param_t *par, u32 par_count, u16 *outbuf);
char *gdg_buffer;
std::string include_dir;
std::string cur_line;
u32 m_cur_addr;
int m_totalSize;
u8 m_cur_pass;
LabelMap labels;
u32 code_line;
bool failed;
std::string last_error_str;
err_t last_error;
typedef std::map<std::string, std::string> AliasMap;
AliasMap aliases;
segment_t cur_segment;
u32 segment_addr[SEGMENT_MAX];
int m_current_param;
const AssemblerSettings settings_;
};
#endif // _DSP_ASSEMBLE_H

View File

@ -0,0 +1,360 @@
/*====================================================================
filename: disassemble.cpp
project: GameCube DSP Tool (gcdsp)
created: 2005.03.04
mail: duddie@walla.com
Copyright (c) 2005 Duddie
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>
#include "Common.h"
#include "FileUtil.h"
#include "disassemble.h"
#include "DSPTables.h"
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif
#ifndef MAX_PATH
#include <Windows.h> // For MAX_PATH
#endif
extern void nop(const UDSPInstruction opc);
DSPDisassembler::DSPDisassembler(const AssemblerSettings &settings)
: settings_(settings)
{
}
DSPDisassembler::~DSPDisassembler()
{
// Some old code for logging unknown ops.
char filename[MAX_PATH];
sprintf(filename, "%sUnkOps.txt", File::GetUserPath(D_DUMPDSP_IDX));
FILE *uo = fopen(filename, "w");
if (!uo)
return;
int count = 0;
for (std::map<u16, int>::const_iterator iter = unk_opcodes.begin();
iter != unk_opcodes.end(); ++iter)
{
if (iter->second > 0)
{
count++;
fprintf(uo, "OP%04x\t%d", iter->first, iter->second);
for (int j = 15; j >= 0; j--) // print op bits
{
if ((j & 0x3) == 3)
fprintf(uo, "\tb");
fprintf(uo, "%d", (iter->first >> j) & 0x1);
}
fprintf(uo, "\n");
}
}
fprintf(uo, "Unknown opcodes count: %d\n", count);
fclose(uo);
}
bool DSPDisassembler::Disassemble(int start_pc, const std::vector<u16> &code, int base_addr, std::string &text)
{
const char *tmp1 = "tmp1.bin";
// First we have to dump the code to a bin file.
FILE *f = fopen(tmp1, "wb");
fwrite(&code[0], 1, code.size() * 2, f);
fclose(f);
// Run the two passes.
return DisFile(tmp1, base_addr, 1, text) && DisFile(tmp1, base_addr, 2, text);
}
char *DSPDisassembler::DisParams(const DSPOPCTemplate& opc, u16 op1, u16 op2, char *strbuf)
{
char *buf = strbuf;
for (int j = 0; j < opc.param_count; j++)
{
if (j > 0)
buf += sprintf(buf, ", ");
u32 val = (opc.params[j].loc >= 1) ? op2 : op1;
val &= opc.params[j].mask;
if (opc.params[j].lshift < 0)
val = val << (-opc.params[j].lshift);
else
val = val >> opc.params[j].lshift;
u32 type = opc.params[j].type;
if ((type & 0xff) == 0x10)
type &= 0xff00;
if (type & P_REG)
{
// Check for _D parameter - if so flip.
if ((type == P_ACC_D) || (type == P_ACCM_D)) // Used to be P_ACCM_D TODO verify
val = (~val & 0x1) | ((type & P_REGS_MASK) >> 8);
else
val |= (type & P_REGS_MASK) >> 8;
type &= ~P_REGS_MASK;
}
switch (type)
{
case P_REG:
if (settings_.decode_registers)
sprintf(buf, "$%s", pdregname(val));
else
sprintf(buf, "$%d", val);
break;
case P_PRG:
if (settings_.decode_registers)
sprintf(buf, "@$%s", pdregname(val));
else
sprintf(buf, "@$%d", val);
break;
case P_VAL:
case P_ADDR_I:
case P_ADDR_D:
if (settings_.decode_names)
{
sprintf(buf, "%s", pdname(val));
}
else
sprintf(buf, "0x%04x", val);
break;
case P_IMM:
if (opc.params[j].size != 2)
{
if (opc.params[j].mask == 0x003f) // LSL, LSR, ASL, ASR
sprintf(buf, "#%d", (val & 0x20) ? (val | 0xFFFFFFC0) : val); // 6-bit sign extension
else
sprintf(buf, "#0x%02x", val);
}
else
{
sprintf(buf, "#0x%04x", val);
}
break;
case P_MEM:
if (opc.params[j].size != 2)
val = (u16)(s16)(s8)val;
if (settings_.decode_names)
sprintf(buf, "@%s", pdname(val));
else
sprintf(buf, "@0x%04x", val);
break;
default:
ERROR_LOG(DSPLLE, "Unknown parameter type: %x", opc.params[j].type);
break;
}
buf += strlen(buf);
}
return strbuf;
}
static void MakeLowerCase(char *ptr)
{
int i = 0;
while (ptr[i])
{
ptr[i] = tolower(ptr[i]);
i++;
}
}
bool DSPDisassembler::DisOpcode(const u16 *binbuf, int base_addr, int pass, u16 *pc, std::string &dest)
{
char buffer[256];
char *buf = buffer;
// Start with 8 spaces, if there's no label.
buf[0] = ' ';
buf[1] = '\0';
buf++;
if ((*pc & 0x7fff) >= 0x1000)
{
++pc;
dest.append("; outside memory");
return false;
}
const u32 op1 = binbuf[*pc & 0x0fff];
const DSPOPCTemplate *opc = NULL;
const DSPOPCTemplate *opc_ext = NULL;
// find opcode
for (int j = 0; j < opcodes_size; j++)
{
u16 mask = opcodes[j].opcode_mask;
if ((op1 & mask) == opcodes[j].opcode)
{
opc = &opcodes[j];
break;
}
}
const DSPOPCTemplate fake_op = {"CW", 0x0000, 0x0000, nop, NULL, 1, 1, {{P_VAL, 2, 0, 0, 0xffff}}, false};
if (!opc)
opc = &fake_op;
bool extended = false;
bool only7bitext = false;
if (((opc->opcode >> 12) == 0x3) && (op1 & 0x007f)) {
extended = true;
only7bitext = true;
}
else if (((opc->opcode >> 12) > 0x3) && (op1 & 0x00ff))
extended = true;
else
extended = false;
if (extended)
{
// opcode has an extension
// find opcode
for (int j = 0; j < opcodes_ext_size; j++)
{
if (only7bitext) {
if (((op1 & 0x7f) & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode)
{
opc_ext = &opcodes_ext[j];
break;
}
}
else {
if ((op1 & opcodes_ext[j].opcode_mask) == opcodes_ext[j].opcode)
{
opc_ext = &opcodes_ext[j];
break;
}
}
}
}
// printing
if (settings_.show_pc)
buf += sprintf(buf, "%04x ", *pc);
u32 op2;
// Size 2 - the op has a large immediate.
if (opc->size == 2)
{
op2 = binbuf[(*pc + 1) & 0x0fff];
if (settings_.show_hex)
buf += sprintf(buf, "%04x %04x ", op1, op2);
}
else
{
op2 = 0;
if (settings_.show_hex)
buf += sprintf(buf, "%04x ", op1);
}
char opname[20];
strcpy(opname, opc->name);
if (settings_.lower_case_ops)
MakeLowerCase(opname);
char ext_buf[20];
if (extended)
sprintf(ext_buf, "%s%c%s", opname, settings_.ext_separator, opc_ext->name);
else
sprintf(ext_buf, "%s", opname);
if (settings_.lower_case_ops)
MakeLowerCase(ext_buf);
if (settings_.print_tabs)
buf += sprintf(buf, "%s\t", ext_buf);
else
buf += sprintf(buf, "%-12s", ext_buf);
if (opc->param_count > 0)
DisParams(*opc, op1, op2, buf);
buf += strlen(buf);
// Handle opcode extension.
if (extended)
{
if (opc->param_count > 0)
buf += sprintf(buf, " ");
buf += sprintf(buf, ": ");
if (opc_ext->param_count > 0)
DisParams(*opc_ext, op1, op2, buf);
buf += strlen(buf);
}
if (opc->opcode_mask == 0)
{
// unknown opcode
unk_opcodes[op1]++;
sprintf(buf, "\t\t; *** UNKNOWN OPCODE ***");
}
if (extended)
*pc += opc_ext->size;
else
*pc += opc->size;
if (pass == 2)
dest.append(buffer);
return true;
}
bool DSPDisassembler::DisFile(const char* name, int base_addr, int pass, std::string &output)
{
FILE* in = fopen(name, "rb");
if (in == NULL)
{
printf("gd_dis_file: No input\n");
return false;
}
int size = (int)File::GetSize(in) & ~1;
u16 *binbuf = new u16[size / 2];
fread(binbuf, 1, size, in);
fclose(in);
// Actually do the disassembly.
for (u16 pc = 0; pc < (size / 2);)
{
DisOpcode(binbuf, base_addr, pass, &pc, output);
if (pass == 2)
output.append("\n");
}
delete [] binbuf;
return true;
}

View File

@ -0,0 +1,87 @@
/*====================================================================
project: GameCube DSP Tool (gcdsp)
created: 2005.03.04
mail: duddie@walla.com
Copyright (c) 2005 Duddie
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.
====================================================================*/
#ifndef _DSP_DISASSEMBLE_H
#define _DSP_DISASSEMBLE_H
#include <map>
#include <vector>
#include "Common.h"
#include "DSPTables.h"
#include "LabelMap.h"
struct AssemblerSettings
{
AssemblerSettings()
: print_tabs(false),
show_hex(false),
show_pc(false),
force(false),
decode_names(true),
decode_registers(true),
ext_separator('\''),
lower_case_ops(true),
pc(0)
{
}
bool print_tabs;
bool show_hex;
bool show_pc;
bool force;
bool decode_names;
bool decode_registers;
char ext_separator;
bool lower_case_ops;
u16 pc;
};
class DSPDisassembler
{
public:
DSPDisassembler(const AssemblerSettings &settings);
~DSPDisassembler();
bool Disassemble(int start_pc, const std::vector<u16> &code, int base_addr, std::string &text);
// Warning - this one is trickier to use right.
// Use pass == 2 if you're just using it by itself.
bool DisOpcode(const u16 *binbuf, int base_addr, int pass, u16 *pc, std::string &dest);
private:
// Moves PC forward and writes the result to dest.
bool DisFile(const char* name, int base_addr, int pass, std::string &output);
char* DisParams(const DSPOPCTemplate& opc, u16 op1, u16 op2, char* strbuf);
std::map<u16, int> unk_opcodes;
const AssemblerSettings settings_;
LabelMap labels;
};
#endif // _DSP_DISASSEMBLE_H

View File

@ -17,11 +17,11 @@
#include "DSPDebugInterface.h"
#include "DSPCore.h"
#include "disassemble.h"
#include "DSP/DSPCore.h"
#include "DSP/disassemble.h"
#include "DSPSymbols.h"
#include "DSPMemoryMap.h"
#include "DSP/DSPMemoryMap.h"
void DSPDebugInterface::disasm(unsigned int address, char *dest, int max_size)
{

View File

@ -17,7 +17,7 @@
#include "Common.h"
#include "Hash.h"
#include "DSPHost.h"
#include "DSP/DSPHost.h"
#include "DSPSymbols.h"
#include "DSPLLETools.h"
#include "../DSP.h"

View File

@ -26,16 +26,16 @@
#include "IniFile.h"
#include "DSPLLEGlobals.h" // Local
#include "DSPInterpreter.h"
#include "DSPHWInterface.h"
#include "disassemble.h"
#include "DSP/DSPInterpreter.h"
#include "DSP/DSPHWInterface.h"
#include "DSP/disassemble.h"
#include "DSPSymbols.h"
#include "AudioCommon.h"
#include "Mixer.h"
#include "DSPTables.h"
#include "DSPCore.h"
#include "DSP/DSPTables.h"
#include "DSP/DSPCore.h"
#include "DSPLLE.h"
#include "../Memmap.h"
#include "../AudioInterface.h"

View File

@ -20,7 +20,7 @@
#include <stdarg.h>
#include "Common.h" // for Common::swap
#include "DSPCore.h"
#include "DSP/DSPCore.h"
#include "DSPLLEGlobals.h"
#if PROFILE

View File

@ -22,11 +22,11 @@
#include "DSPLLEGlobals.h"
#include "FileUtil.h"
#include "DSPCore.h"
#include "DSPCodeUtil.h"
#include "DSP/DSPCore.h"
#include "DSP/DSPCodeUtil.h"
#include "DSPLLETools.h"
#include "disassemble.h"
#include "DSPInterpreter.h"
#include "DSP/disassemble.h"
#include "DSP/DSPInterpreter.h"
bool DumpDSPCode(const u8 *code_be, int size_in_bytes, u32 crc)
{

View File

@ -26,9 +26,9 @@
#include "Common.h"
#include "StringUtil.h"
#include "DSPCore.h"
#include "DSP/DSPCore.h"
#include "DSPSymbols.h"
#include "disassemble.h"
#include "DSP/disassemble.h"
namespace DSPSymbols {

View File

@ -6,8 +6,6 @@ import sys
files = [
"ActionReplay.cpp",
"ARDecrypt.cpp",
"GeckoCode.cpp",
"GeckoCodeConfig.cpp",
"ConfigManager.cpp",
"Console.cpp",
"Core.cpp",
@ -32,6 +30,37 @@ files = [
"Debugger/Debugger_SymbolMap.cpp",
"Debugger/Dump.cpp",
"Debugger/PPCDebugInterface.cpp",
"DSP/assemble.cpp",
"DSP/disassemble.cpp",
"DSP/DSPAccelerator.cpp",
"DSP/DSPIntCCUtil.cpp",
"DSP/DSPIntExtOps.cpp",
"DSP/DSPHWInterface.cpp",
"DSP/DSPMemoryMap.cpp",
"DSP/DSPStacks.cpp",
"DSP/DSPAnalyzer.cpp",
"DSP/DspIntArithmetic.cpp",
"DSP/DspIntBranch.cpp",
"DSP/DspIntLoadStore.cpp",
"DSP/DspIntMisc.cpp",
"DSP/DspIntMultiplier.cpp",
"DSP/DSPEmitter.cpp",
"DSP/DSPCodeUtil.cpp",
"DSP/LabelMap.cpp",
"DSP/DSPInterpreter.cpp",
"DSP/DSPCore.cpp",
"DSP/DSPTables.cpp",
"DSP/Jit/DSPJitExtOps.cpp",
"DSP/Jit/DSPJitBranch.cpp",
"DSP/Jit/DSPJitUtil.cpp",
"DSP/Jit/DSPJitCCUtil.cpp",
"DSP/Jit/DSPJitArithmetic.cpp",
"DSP/Jit/DSPJitLoadStore.cpp",
"DSP/Jit/DSPJitMultiplier.cpp",
"DSP/Jit/DSPJitMisc.cpp",
"DSP/Jit/DSPJitRegCache.cpp",
"GeckoCode.cpp",
"GeckoCodeConfig.cpp",
"HLE/HLE.cpp",
"HLE/HLE_Misc.cpp",
"HLE/HLE_OS.cpp",
@ -165,7 +194,7 @@ if sys.platform == 'win32':
elif sys.platform == 'darwin':
files += [ "HW/BBA-TAP/TAP_Apple.cpp", "HW/WiimoteReal/IOdarwin.mm" ]
elif sys.platform == 'linux2' and env['HAVE_BLUEZ']:
files += [ "HW/BBA-TAP/TAP_Unix.cpp", "HW/WiimoteReal/IONix.cpp" ]
files += [ "HW/BBA-TAP/TAP_Unix.cpp", "HW/WiimoteReal/IODummy.cpp" ]
else:
files += [ "HW/BBA-TAP/TAP_Unix.cpp", "HW/WiimoteReal/IODummy.cpp" ]