ppsspp/Core/MIPS/MIPSAsm.cpp
2013-07-29 00:52:01 +10:00

411 lines
8.9 KiB
C++

#ifdef _WIN32
#include "stdafx.h"
#endif
#include "MIPSAsm.h"
namespace MIPSAsm
{
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(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(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(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(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)
{
// PrintError(ERROR_ERROR,"Parameter failure \"%s\"",Params);
} else {
// PrintError(ERROR_ERROR,"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) & 0x3FFFFFF;
} else if (Opcode.flags & O_IPCR) // relative 16 bit value
{
int num = (Vars.Immediate-RamPos-4);
if (num > 0x20000 || num < (-0x20000))
{
//QueueError(ERROR_ERROR,"Branch target %08X out of range",Vars.Immediate);
return false;
}
Vars.Immediate = num >> 2;
}
switch (Vars.ImmediateType)
{
case MIPS_IMMEDIATE5:
if (Vars.Immediate > 0x1F)
{
// QueueError(ERROR_ERROR,"Immediate value %02X out of range",Vars.OriginalImmediate);
return false;
}
break;
Vars.Immediate &= 0x1F;
break;
case MIPS_IMMEDIATE16:
if (abs(Vars.Immediate) > 0xFFFF)
{
// QueueError(ERROR_ERROR,"Immediate value %04X out of range",Vars.OriginalImmediate);
return false;
}
Vars.Immediate &= 0xFFFF;
break;
case MIPS_IMMEDIATE20:
if (abs(Vars.Immediate) > 0xFFFFF)
{
// QueueError(ERROR_ERROR,"Immediate value %08X out of range",Vars.OriginalImmediate);
return false;
}
Vars.Immediate &= 0xFFFFF;
break;
case MIPS_IMMEDIATE26:
if (abs(Vars.Immediate) > 0x3FFFFFF)
{
// QueueError(ERROR_ERROR,"Immediate value %08X out of range",Vars.OriginalImmediate);
return false;
}
Vars.Immediate &= 0x3FFFFFF;
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 & 0x1F) << 6);
break;
case MIPS_IMMEDIATE16:
encoding |= (Vars.Immediate & 0xFFFF);
break;
case MIPS_IMMEDIATE20:
encoding |= (Vars.Immediate & 0xFFFFF) << 6;
break;
case MIPS_IMMEDIATE26:
encoding |= Vars.Immediate & 0x3FFFFFF;
break;
}
}
}