mirror of
https://github.com/libretro/ppsspp.git
synced 2024-12-14 20:48:30 +00:00
427 lines
9.1 KiB
C++
427 lines
9.1 KiB
C++
#ifdef _WIN32
|
|
#include "stdafx.h"
|
|
#endif
|
|
#include "MIPSAsm.h"
|
|
|
|
namespace MIPSAsm
|
|
{
|
|
|
|
char errorMessage[512];
|
|
|
|
void SetAssembleError(char* format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args,format);
|
|
vsprintf(errorMessage,format,args);
|
|
va_end (args);
|
|
}
|
|
|
|
char* GetAssembleError()
|
|
{
|
|
return errorMessage;
|
|
}
|
|
|
|
void SplitLine(const char* Line, char* Name, char* Arguments)
|
|
{
|
|
while (*Line == ' ' || *Line == '\t') Line++;
|
|
while (*Line != ' ')
|
|
{
|
|
if (*Line == 0)
|
|
{
|
|
*Name = 0;
|
|
*Arguments = 0;
|
|
return;
|
|
}
|
|
*Name++ = *Line++;
|
|
}
|
|
*Name = 0;
|
|
|
|
while (*Line == ' ' || *Line == '\t') Line++;
|
|
|
|
while (*Line != 0)
|
|
{
|
|
*Arguments++ = *Line++;
|
|
}
|
|
*Arguments = 0;
|
|
}
|
|
|
|
bool MipsAssembleOpcode(const char* line, DebugInterface* cpu, u32 address, u32& dest)
|
|
{
|
|
char name[64],args[256];
|
|
SplitLine(line,name,args);
|
|
|
|
CMipsInstruction Opcode(cpu);
|
|
if (cpu == NULL || Opcode.Load(name,args,(int)address) == false)
|
|
{
|
|
return false;
|
|
}
|
|
if (Opcode.Validate() == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Opcode.Encode();
|
|
dest = Opcode.getEncoding();
|
|
|
|
return true;
|
|
}
|
|
|
|
int MipsGetRegister(const char* source, int& RetLen)
|
|
{
|
|
for (int z = 0; MipsRegister[z].name != NULL; z++)
|
|
{
|
|
int len = MipsRegister[z].len;
|
|
if (strncmp(MipsRegister[z].name,source,len) == 0) // fine so far
|
|
{
|
|
if (source[len] == ',' || source[len] == '\n' || source[len] == 0
|
|
|| source[len] == ')' || source[len] == '(' || source[len] == '-') // one of these HAS TO come after a register
|
|
{
|
|
RetLen = len;
|
|
return MipsRegister[z].num;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int MipsGetFloatRegister(const char* source, int& RetLen)
|
|
{
|
|
for (int z = 0; MipsFloatRegister[z].name != NULL; z++)
|
|
{
|
|
int len = MipsFloatRegister[z].len;
|
|
if (strncmp(MipsFloatRegister[z].name,source,len) == 0) // fine so far
|
|
{
|
|
if (source[len] == ',' || source[len] == '\n' || source[len] == 0
|
|
|| source[len] == ')' || source[len] == '(' || source[len] == '-') // one of these HAS TO come after a register
|
|
{
|
|
RetLen = len;
|
|
return MipsFloatRegister[z].num;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool MipsCheckImmediate(const char* Source, char* Dest, int& RetLen)
|
|
{
|
|
int BufferPos = 0;
|
|
int l;
|
|
|
|
if (MipsGetRegister(Source,l) != -1) // there's a register -> no immediate
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int SourceLen = 0;
|
|
|
|
while (true)
|
|
{
|
|
if (*Source == '\'' && *(Source+2) == '\'')
|
|
{
|
|
Dest[BufferPos++] = *Source++;
|
|
Dest[BufferPos++] = *Source++;
|
|
Dest[BufferPos++] = *Source++;
|
|
SourceLen+=3;
|
|
continue;
|
|
}
|
|
|
|
if (*Source == 0 || *Source == '\n' || *Source == ',')
|
|
{
|
|
Dest[BufferPos] = 0;
|
|
break;
|
|
}
|
|
if ( *Source == ' ' || *Source == '\t')
|
|
{
|
|
Source++;
|
|
SourceLen++;
|
|
continue;
|
|
}
|
|
|
|
|
|
if (*Source == '(') // could also be part of the opcode, ie (r4)
|
|
{
|
|
if (MipsGetRegister(Source+1,l) != -1) // stop if it is
|
|
{
|
|
Dest[BufferPos] = 0;
|
|
break;
|
|
}
|
|
}
|
|
Dest[BufferPos++] = *Source++;
|
|
SourceLen++;
|
|
}
|
|
|
|
if (BufferPos == 0) return false;
|
|
|
|
RetLen = SourceLen;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool CMipsInstruction::Load(char* Name, char* Params, int RamPos)
|
|
{
|
|
bool paramfail = false;
|
|
NoCheckError = false;
|
|
this->RamPos = RamPos;
|
|
|
|
for (int z = 0; MipsOpcodes[z].name != NULL; z++)
|
|
{
|
|
if (strcmp(Name,MipsOpcodes[z].name) == 0)
|
|
{
|
|
if (LoadEncoding(MipsOpcodes[z],Params) == true)
|
|
{
|
|
Loaded = true;
|
|
return true;
|
|
}
|
|
paramfail = true;
|
|
}
|
|
}
|
|
|
|
if (NoCheckError == false)
|
|
{
|
|
if (paramfail == true)
|
|
{
|
|
SetAssembleError("Parameter failure \"%s\"",Params);
|
|
} else {
|
|
SetAssembleError("Invalid opcode \"%s\"",Name);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CMipsInstruction::LoadEncoding(const tMipsOpcode& SourceOpcode, char* Line)
|
|
{
|
|
char ImmediateBuffer[512];
|
|
|
|
int RetLen;
|
|
bool Immediate = false;
|
|
|
|
char* SourceEncoding = SourceOpcode.encoding;
|
|
char* OriginalLine = Line;
|
|
|
|
while (*Line == ' ' || *Line == '\t') Line++;
|
|
|
|
if (!(*SourceEncoding == 0 && *Line == 0))
|
|
{
|
|
while (*SourceEncoding != NULL)
|
|
{
|
|
while (*Line == ' ' || *Line == '\t') Line++;
|
|
if (*Line == 0) return false;
|
|
|
|
switch (*SourceEncoding)
|
|
{
|
|
case 'T': // float reg
|
|
if ((Vars.rt = MipsGetFloatRegister(Line,RetLen)) == -1) return false;
|
|
Line += RetLen;
|
|
SourceEncoding++;
|
|
break;
|
|
case 'D': // float reg
|
|
if ((Vars.rd = MipsGetFloatRegister(Line,RetLen)) == -1) return false;
|
|
Line += RetLen;
|
|
SourceEncoding++;
|
|
break;
|
|
case 'S': // float reg
|
|
if ((Vars.rs = MipsGetFloatRegister(Line,RetLen)) == -1) return false;
|
|
Line += RetLen;
|
|
SourceEncoding++;
|
|
break;
|
|
case 't':
|
|
if ((Vars.rt = MipsGetRegister(Line,RetLen)) == -1) return false;
|
|
Line += RetLen;
|
|
SourceEncoding++;
|
|
break;
|
|
case 'd':
|
|
if ((Vars.rd = MipsGetRegister(Line,RetLen)) == -1) return false;
|
|
Line += RetLen;
|
|
SourceEncoding++;
|
|
break;
|
|
case 's':
|
|
if ((Vars.rs = MipsGetRegister(Line,RetLen)) == -1) return false;
|
|
Line += RetLen;
|
|
SourceEncoding++;
|
|
break;
|
|
case 'i': // 16 bit immediate
|
|
case 'I': // 32 bit immediate
|
|
case 'a': // 5 bit immediate
|
|
case 'b': // 20 bit immediate
|
|
if (MipsCheckImmediate(Line,ImmediateBuffer,RetLen) == false) return false;
|
|
Immediate = true;
|
|
Line += RetLen;
|
|
SourceEncoding++;
|
|
break;
|
|
case 'r': // forced register
|
|
if (MipsGetRegister(Line,RetLen) != *(SourceEncoding+1)) return false;
|
|
Line += RetLen;
|
|
SourceEncoding += 2;
|
|
break;
|
|
default: // everything else
|
|
if (*SourceEncoding++ != *Line++) return false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (*Line == ' ' || *Line == '\t') Line++;
|
|
if (*Line != 0) return false; // there's something else at the end, bad
|
|
|
|
// the opcode is fine - now set all remaining flags
|
|
Opcode = SourceOpcode;
|
|
|
|
if (Immediate == true)
|
|
{
|
|
u32 imm;
|
|
PostfixExpression postfix;
|
|
if (cpu->initExpression(ImmediateBuffer,postfix) == false) return false;
|
|
if (cpu->parseExpression(postfix,imm) == false) return false;
|
|
|
|
Vars.Immediate = (int) imm;
|
|
if (Opcode.flags & O_I5)
|
|
{
|
|
Vars.ImmediateType = MIPS_IMMEDIATE5;
|
|
} else if (Opcode.flags & O_I16)
|
|
{
|
|
Vars.ImmediateType = MIPS_IMMEDIATE16;
|
|
} else if (Opcode.flags & O_I20)
|
|
{
|
|
Vars.ImmediateType = MIPS_IMMEDIATE20;
|
|
} else if (Opcode.flags & O_I26)
|
|
{
|
|
Vars.ImmediateType = MIPS_IMMEDIATE26;
|
|
}
|
|
} else {
|
|
Vars.ImmediateType = MIPS_NOIMMEDIATE;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMipsInstruction::Validate()
|
|
{
|
|
if (RamPos % 4)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// check immediates
|
|
if (Vars.ImmediateType != MIPS_NOIMMEDIATE)
|
|
{
|
|
Vars.OriginalImmediate = Vars.Immediate;
|
|
|
|
if (Opcode.flags & O_IPCA) // absolute value >> 2)
|
|
{
|
|
Vars.Immediate = (Vars.Immediate >> 2) & 0x03FFFFFF;
|
|
} else if (Opcode.flags & O_IPCR) // relative 16 bit value
|
|
{
|
|
int num = (Vars.Immediate-RamPos-4);
|
|
|
|
if (num > 0x20000 || num < (-0x20000))
|
|
{
|
|
SetAssembleError("Branch target %08X out of range",Vars.Immediate);
|
|
return false;
|
|
}
|
|
Vars.Immediate = num >> 2;
|
|
}
|
|
|
|
switch (Vars.ImmediateType)
|
|
{
|
|
case MIPS_IMMEDIATE5:
|
|
if (Vars.Immediate > 0x1F)
|
|
{
|
|
SetAssembleError("Immediate value %02X out of range",Vars.OriginalImmediate);
|
|
return false;
|
|
}
|
|
break;
|
|
Vars.Immediate &= 0x1F;
|
|
break;
|
|
case MIPS_IMMEDIATE16:
|
|
if (abs(Vars.Immediate) > 0x0000FFFF)
|
|
{
|
|
SetAssembleError("Immediate value %04X out of range",Vars.OriginalImmediate);
|
|
return false;
|
|
}
|
|
Vars.Immediate &= 0x0000FFFF;
|
|
break;
|
|
case MIPS_IMMEDIATE20:
|
|
if (abs(Vars.Immediate) > 0x000FFFFF)
|
|
{
|
|
SetAssembleError("Immediate value %08X out of range",Vars.OriginalImmediate);
|
|
return false;
|
|
}
|
|
Vars.Immediate &= 0x000FFFFF;
|
|
break;
|
|
case MIPS_IMMEDIATE26:
|
|
if (abs(Vars.Immediate) > 0x03FFFFFF)
|
|
{
|
|
SetAssembleError("Immediate value %08X out of range",Vars.OriginalImmediate);
|
|
return false;
|
|
}
|
|
Vars.Immediate &= 0x03FFFFFF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMipsInstruction::Encode()
|
|
{
|
|
encoding = (u32) Opcode.destencoding;
|
|
|
|
if (Opcode.flags & O_RS) encoding |= (Vars.rs << 21); // source reg
|
|
if (Opcode.flags & O_RT) encoding |= (Vars.rt << 16); // target reg
|
|
if (Opcode.flags & O_RD) encoding |= (Vars.rd << 11); // dest reg
|
|
if (Opcode.flags & O_RSD) // s = d & s
|
|
{
|
|
encoding |= (Vars.rs << 21);
|
|
encoding |= (Vars.rs << 11);
|
|
}
|
|
if (Opcode.flags & O_RST) // s = t & s
|
|
{
|
|
encoding |= (Vars.rs << 21);
|
|
encoding |= (Vars.rs << 16);
|
|
}
|
|
if (Opcode.flags & O_RDT) // d = t & d
|
|
{
|
|
encoding |= (Vars.rd << 16);
|
|
encoding |= (Vars.rd << 11);
|
|
}
|
|
|
|
if (Opcode.flags & MO_FRT) encoding |= (Vars.rt << 16); // float target
|
|
if (Opcode.flags & MO_FRS) encoding |= (Vars.rs << 11); // float source
|
|
if (Opcode.flags & MO_FRD) encoding |= (Vars.rd << 6); // float target
|
|
if (Opcode.flags & MO_FRSD) // s = d & s
|
|
{
|
|
encoding |= (Vars.rs << 6);
|
|
encoding |= (Vars.rs << 11);
|
|
}
|
|
if (Opcode.flags & MO_FRST) // s = t & s
|
|
{
|
|
encoding |= (Vars.rs << 11);
|
|
encoding |= (Vars.rs << 16);
|
|
}
|
|
if (Opcode.flags & MO_FRDT) // d = t & d
|
|
{
|
|
encoding |= (Vars.rd << 6);
|
|
encoding |= (Vars.rd << 16);
|
|
}
|
|
|
|
switch (Vars.ImmediateType)
|
|
{
|
|
case MIPS_IMMEDIATE5:
|
|
encoding |= (Vars.Immediate & 0x0000001F) << 6;
|
|
break;
|
|
case MIPS_IMMEDIATE16:
|
|
encoding |= (Vars.Immediate & 0x0000FFFF);
|
|
break;
|
|
case MIPS_IMMEDIATE20:
|
|
encoding |= (Vars.Immediate & 0x000FFFFF) << 6;
|
|
break;
|
|
case MIPS_IMMEDIATE26:
|
|
encoding |= (Vars.Immediate & 0x03FFFFFF);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|