Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@
# Visual Studio CMake directories.
/.vs/
/out/

# Non-Windows CMake directory.
/build/
49 changes: 42 additions & 7 deletions Archs/MIPS/CMipsInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ CMipsInstruction::CMipsInstruction(MipsOpcodeData& opcode, MipsImmediateData& im

addNop = false;
IgnoreLoadDelay = Mips.GetIgnoreDelay();
RelaxImmediateSign = Mips.GetRelaxImmediateSign();
}

CMipsInstruction::~CMipsInstruction()
Expand Down Expand Up @@ -132,7 +133,7 @@ bool CMipsInstruction::Validate(const ValidateState &state)
{
int num = (int) (immediateData.primary.value-RamPos-4);

if (num > 0x20000 || num < (-0x20000))
if (num >= 0x20000 || num < (-0x20000))
{
Logger::queueError(Logger::Error, "Branch target %08X out of range",immediateData.primary.value);
return false;
Expand All @@ -156,16 +157,50 @@ bool CMipsInstruction::Validate(const ValidateState &state)
}

int immediateBits = getImmediateBits(immediateData.primary.type);
unsigned int mask = (0xFFFFFFFF << (32-immediateBits)) >> (32-immediateBits);
int digits = (immediateBits+3) / 4;

if ((unsigned int)std::abs(immediateData.primary.value) > mask)
{
Logger::queueError(Logger::Error, "Immediate value 0x%0*X out of range",digits,immediateData.primary.value);
return false;
// Example: 8 bit immediate. If signed, -0x80 to 0x7F. If unsigned, 0x00 to 0xFF. If relaxed, -0x80 to 0xFF.
int maxInclusiveUns = (1 << immediateBits) - 1;
int maxInclusiveSgn = (1 << (immediateBits - 1)) - 1;
int minInclusiveUns = 0;
int minInclusiveSgn = -(1 << (immediateBits - 1));

int minInclusiveStrict, maxInclusiveStrict;
if ((opcodeData.opcode.flags & MO_IMMUNSIGNED)) {
minInclusiveStrict = minInclusiveUns;
maxInclusiveStrict = maxInclusiveUns;
} else {
minInclusiveStrict = minInclusiveSgn;
maxInclusiveStrict = maxInclusiveSgn;
}

if (immediateData.primary.value >= minInclusiveStrict && immediateData.primary.value <= maxInclusiveStrict) {
// In strict range, no problem.
(void)0;
} else if (immediateData.primary.value >= minInclusiveSgn && immediateData.primary.value <= maxInclusiveUns) {
// In relaxed range.
if (!RelaxImmediateSign) {
Logger::queueError(Logger::Error, "Immediate value 0x%0*X out of range 0x%0*X - 0x%0*X, .relaximmsign to suppress",
digits, immediateData.primary.value,
digits, minInclusiveStrict,
digits, maxInclusiveStrict);
}
} else {
// Entirely out of range.
if (RelaxImmediateSign) {
Logger::queueError(Logger::Error, "Immediate value 0x%0*X out of relaxed range 0x%0*X - 0x%0*X",
digits, immediateData.primary.value,
digits, minInclusiveSgn,
digits, maxInclusiveUns);
} else {
Logger::queueError(Logger::Error, "Immediate value 0x%0*X out of range 0x%0*X - 0x%0*X",
digits, immediateData.primary.value,
digits, minInclusiveStrict,
digits, maxInclusiveStrict);
}
}

immediateData.primary.value &= mask;
immediateData.primary.value &= maxInclusiveUns;
}

if (immediateData.secondary.type != MipsImmediateType::None)
Expand Down
1 change: 1 addition & 0 deletions Archs/MIPS/CMipsInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class CMipsInstruction: public CAssemblerCommand
int floatToHalfFloat(int i);

bool IgnoreLoadDelay;
bool RelaxImmediateSign;
int64_t RamPos;
bool addNop;

Expand Down
1 change: 1 addition & 0 deletions Archs/MIPS/Mips.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ CMipsArchitecture::CMipsArchitecture()
LoadDelay = false;
LoadDelayRegister = 0;
DelaySlot = false;
RelaxImmediateSign = false;
Version = MARCH_INVALID;
}

Expand Down
3 changes: 3 additions & 0 deletions Archs/MIPS/Mips.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ class CMipsArchitecture: public Architecture
bool GetDelaySlot() { return DelaySlot; };
void SetDelaySlot(bool b) {DelaySlot = b; };
bool hasLoadDelay() { return Version == MARCH_PSX; };
bool GetRelaxImmediateSign() { return RelaxImmediateSign; }
void SetRelaxImmediateSign(bool b) { RelaxImmediateSign = b; }
private:
bool FixLoadDelay;
bool IgnoreLoadDelay;
bool LoadDelay;
int LoadDelayRegister;
bool DelaySlot;
bool RelaxImmediateSign;
MipsArchType Version;
};

Expand Down
58 changes: 29 additions & 29 deletions Archs/MIPS/MipsOpcodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ const tMipsOpcode MipsOpcodes[] = {
{ "slti", "s,i16", MIPS_OP(0x0A), MA_MIPS1, MO_RST },
{ "sltiu", "t,s,i16", MIPS_OP(0x0B), MA_MIPS1, MO_IGNORERTD },
{ "sltiu", "s,i16", MIPS_OP(0x0B), MA_MIPS1, MO_RST },
{ "andi", "t,s,i16", MIPS_OP(0x0C), MA_MIPS1, MO_IGNORERTD },
{ "andi", "s,i16", MIPS_OP(0x0C), MA_MIPS1, MO_RST },
{ "ori", "t,s,i16", MIPS_OP(0x0D), MA_MIPS1, MO_IGNORERTD },
{ "ori", "s,i16", MIPS_OP(0x0D), MA_MIPS1, MO_RST },
{ "xori", "t,s,i16", MIPS_OP(0x0E), MA_MIPS1, MO_IGNORERTD },
{ "xori", "s,i16", MIPS_OP(0x0E), MA_MIPS1, MO_RST },
{ "lui", "t,i16", MIPS_OP(0x0F), MA_MIPS1, MO_IGNORERTD },
{ "andi", "t,s,i16", MIPS_OP(0x0C), MA_MIPS1, MO_IGNORERTD|MO_IMMUNSIGNED },
{ "andi", "s,i16", MIPS_OP(0x0C), MA_MIPS1, MO_RST|MO_IMMUNSIGNED },
{ "ori", "t,s,i16", MIPS_OP(0x0D), MA_MIPS1, MO_IGNORERTD|MO_IMMUNSIGNED },
{ "ori", "s,i16", MIPS_OP(0x0D), MA_MIPS1, MO_RST|MO_IMMUNSIGNED },
{ "xori", "t,s,i16", MIPS_OP(0x0E), MA_MIPS1, MO_IGNORERTD|MO_IMMUNSIGNED },
{ "xori", "s,i16", MIPS_OP(0x0E), MA_MIPS1, MO_RST|MO_IMMUNSIGNED },
{ "lui", "t,i16", MIPS_OP(0x0F), MA_MIPS1, MO_IGNORERTD|MO_IMMUNSIGNED },
{ "cop2", "i25", MIPS_OP(0x12)|(1<<25), MA_PSX, 0 },
{ "beql", "s,t,i16", MIPS_OP(0x14), MA_MIPS2, MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
{ "beqzl", "s,i16", MIPS_OP(0x14), MA_MIPS2, MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
Expand Down Expand Up @@ -172,15 +172,15 @@ const tMipsOpcode MipsOpcodes[] = {
// *3: dsrlv on PS2, clz on PSP *4: dsrav on PS2, clo on PSP
// *5: dadd on PS2, max on PSP *6: daddu on PS2, min on PSP
// *7: dsub on PS2, msub on PSP *8: dsubu on PS2, msubu on PSP
{ "sll", "d,t,i5", MIPS_SPECIAL(0x00), MA_MIPS1, 0 },
{ "sll", "d,i5", MIPS_SPECIAL(0x00), MA_MIPS1, MO_RDT },
{ "sll", "d,t,i5", MIPS_SPECIAL(0x00), MA_MIPS1, MO_IMMUNSIGNED },
{ "sll", "d,i5", MIPS_SPECIAL(0x00), MA_MIPS1, MO_RDT|MO_IMMUNSIGNED },
{ "nop", "", MIPS_SPECIAL(0x00), MA_MIPS1, 0 },
{ "srl", "d,t,i5", MIPS_SPECIAL(0x02), MA_MIPS1, 0 },
{ "srl", "d,i5", MIPS_SPECIAL(0x02), MA_MIPS1, MO_RDT },
{ "rotr", "d,t,i5", MIPS_SPECIAL(0x02)|MIPS_RS(1), MA_PSP, 0 },
{ "rotr", "d,i5", MIPS_SPECIAL(0x02)|MIPS_RS(1), MA_PSP, MO_RDT },
{ "sra", "d,t,i5", MIPS_SPECIAL(0x03), MA_MIPS1, 0 },
{ "sra", "d,i5", MIPS_SPECIAL(0x03), MA_MIPS1, MO_RDT },
{ "srl", "d,t,i5", MIPS_SPECIAL(0x02), MA_MIPS1, MO_IMMUNSIGNED },
{ "srl", "d,i5", MIPS_SPECIAL(0x02), MA_MIPS1, MO_RDT|MO_IMMUNSIGNED },
{ "rotr", "d,t,i5", MIPS_SPECIAL(0x02)|MIPS_RS(1), MA_PSP, MO_IMMUNSIGNED },
{ "rotr", "d,i5", MIPS_SPECIAL(0x02)|MIPS_RS(1), MA_PSP, MO_RDT|MO_IMMUNSIGNED },
{ "sra", "d,t,i5", MIPS_SPECIAL(0x03), MA_MIPS1, MO_IMMUNSIGNED },
{ "sra", "d,i5", MIPS_SPECIAL(0x03), MA_MIPS1, MO_RDT|MO_IMMUNSIGNED },
{ "sllv", "d,t,s", MIPS_SPECIAL(0x04), MA_MIPS1, 0 },
{ "sllv", "d,s", MIPS_SPECIAL(0x04), MA_MIPS1, MO_RDT },
{ "srlv", "d,t,s", MIPS_SPECIAL(0x06), MA_MIPS1, 0 },
Expand Down Expand Up @@ -291,18 +291,18 @@ const tMipsOpcode MipsOpcodes[] = {
{ "teq", "s,t", MIPS_SPECIAL(0x34), MA_MIPS2, 0 },
{ "tne", "s,t,i10", MIPS_SPECIAL(0x36), MA_MIPS2, 0 },
{ "tne", "s,t", MIPS_SPECIAL(0x36), MA_MIPS2, 0 },
{ "dsll", "d,t,i5", MIPS_SPECIAL(0x38), MA_MIPS3, MO_64BIT },
{ "dsll", "d,i5", MIPS_SPECIAL(0x38), MA_MIPS3, MO_64BIT|MO_RDT },
{ "dsrl", "d,t,i5", MIPS_SPECIAL(0x3A), MA_MIPS3, MO_64BIT },
{ "dsrl", "d,i5", MIPS_SPECIAL(0x3A), MA_MIPS3, MO_64BIT|MO_RDT },
{ "dsra", "d,t,i5", MIPS_SPECIAL(0x3B), MA_MIPS3, MO_64BIT },
{ "dsra", "d,i5", MIPS_SPECIAL(0x3B), MA_MIPS3, MO_64BIT|MO_RDT },
{ "dsll32", "d,t,i5", MIPS_SPECIAL(0x3C), MA_MIPS3, MO_64BIT },
{ "dsll32", "d,i5", MIPS_SPECIAL(0x3C), MA_MIPS3, MO_64BIT|MO_RDT },
{ "dsrl32", "d,t,i5", MIPS_SPECIAL(0x3E), MA_MIPS3, MO_64BIT },
{ "dsrl32", "d,i5", MIPS_SPECIAL(0x3E), MA_MIPS3, MO_64BIT|MO_RDT },
{ "dsra32", "d,t,i5", MIPS_SPECIAL(0x3F), MA_MIPS3, MO_64BIT },
{ "dsra32", "d,i5", MIPS_SPECIAL(0x3F), MA_MIPS3, MO_64BIT|MO_RDT },
{ "dsll", "d,t,i5", MIPS_SPECIAL(0x38), MA_MIPS3, MO_64BIT|MO_IMMUNSIGNED },
{ "dsll", "d,i5", MIPS_SPECIAL(0x38), MA_MIPS3, MO_64BIT|MO_RDT|MO_IMMUNSIGNED },
{ "dsrl", "d,t,i5", MIPS_SPECIAL(0x3A), MA_MIPS3, MO_64BIT|MO_IMMUNSIGNED },
{ "dsrl", "d,i5", MIPS_SPECIAL(0x3A), MA_MIPS3, MO_64BIT|MO_RDT|MO_IMMUNSIGNED },
{ "dsra", "d,t,i5", MIPS_SPECIAL(0x3B), MA_MIPS3, MO_64BIT|MO_IMMUNSIGNED },
{ "dsra", "d,i5", MIPS_SPECIAL(0x3B), MA_MIPS3, MO_64BIT|MO_RDT|MO_IMMUNSIGNED },
{ "dsll32", "d,t,i5", MIPS_SPECIAL(0x3C), MA_MIPS3, MO_64BIT|MO_IMMUNSIGNED },
{ "dsll32", "d,i5", MIPS_SPECIAL(0x3C), MA_MIPS3, MO_64BIT|MO_RDT|MO_IMMUNSIGNED },
{ "dsrl32", "d,t,i5", MIPS_SPECIAL(0x3E), MA_MIPS3, MO_64BIT|MO_IMMUNSIGNED },
{ "dsrl32", "d,i5", MIPS_SPECIAL(0x3E), MA_MIPS3, MO_64BIT|MO_RDT|MO_IMMUNSIGNED },
{ "dsra32", "d,t,i5", MIPS_SPECIAL(0x3F), MA_MIPS3, MO_64BIT|MO_IMMUNSIGNED },
{ "dsra32", "d,i5", MIPS_SPECIAL(0x3F), MA_MIPS3, MO_64BIT|MO_RDT|MO_IMMUNSIGNED },

// REGIMM: encoded by the rt field when opcode field = REGIMM.
// 31---------26----------20-------16------------------------------0
Expand All @@ -319,9 +319,9 @@ const tMipsOpcode MipsOpcodes[] = {
{ "bltzl", "s,i16", MIPS_REGIMM(0x02), MA_MIPS2, MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
{ "bgezl", "s,i16", MIPS_REGIMM(0x03), MA_MIPS2, MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
{ "tgei", "s,i16", MIPS_REGIMM(0x08), MA_MIPS2, 0 },
{ "tgeiu", "s,i16", MIPS_REGIMM(0x09), MA_MIPS2, 0 },
{ "tgeiu", "s,i16", MIPS_REGIMM(0x09), MA_MIPS2, MO_IMMUNSIGNED },
{ "tlti", "s,i16", MIPS_REGIMM(0x0A), MA_MIPS2, 0 },
{ "tltiu", "s,i16", MIPS_REGIMM(0x0B), MA_MIPS2, 0 },
{ "tltiu", "s,i16", MIPS_REGIMM(0x0B), MA_MIPS2, MO_IMMUNSIGNED },
{ "teqi", "s,i16", MIPS_REGIMM(0x0C), MA_MIPS2, 0 },
{ "tnei", "s,i16", MIPS_REGIMM(0x0E), MA_MIPS2, 0 },
{ "bltzal", "s,i16", MIPS_REGIMM(0x10), MA_MIPS1, MO_IPCR|MO_DELAY|MO_NODELAYSLOT },
Expand Down
1 change: 1 addition & 0 deletions Archs/MIPS/MipsOpcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define MO_64BIT (1 << 12) // only available on 64 bit cpus
#define MO_FPU (1 << 13) // only available with an fpu
#define MO_DFPU (1 << 14) // double-precision fpu opcodes
#define MO_IMMUNSIGNED (1 << 15) // immediate is interpreted as unsigned

#define MO_VFPU (1 << 16) // vfpu type opcode
#define MO_VFPU_MIXED (1 << 17) // mixed mode vfpu register
Expand Down
25 changes: 25 additions & 0 deletions Parser/DirectivesParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,30 @@ std::unique_ptr<CAssemblerCommand> parseDirectiveSym(Parser& parser, int flags)
return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveRelaxImmSign(Parser& parser, int flags)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MIPS specific directives could go into https://github.com/Kingcom/armips/blob/master/Archs/MIPS/MipsParser.cpp#L212 - unless we would want to make this a general feature for all architectures. But as it's mostly a legacy behavior for MIPS it's probably not needed.

{
auto stringValue = getStringOrIdentifier(parser);
if (!stringValue)
return nullptr;

Architecture& arch = Architecture::current();
CMipsArchitecture* mipsArch = dynamic_cast<CMipsArchitecture*>(&arch);
if (!mipsArch)
return nullptr;

if (stringValue == "on")
{
mipsArch->SetRelaxImmediateSign(true);
return std::make_unique<DummyCommand>();
} else if (stringValue == "off")
{
mipsArch->SetRelaxImmediateSign(false);
return std::make_unique<DummyCommand>();
}

return nullptr;
}

std::unique_ptr<CAssemblerCommand> parseDirectiveDefineLabel(Parser& parser, int flags, std::optional<bool> thumbMode)
{
const Token& tok = parser.nextToken();
Expand Down Expand Up @@ -841,6 +865,7 @@ const DirectiveMap directives = {
{ ".relativeinclude", { &parseDirectiveRelativeInclude, 0 } },
{ ".nocash", { &parseDirectiveNocash, 0 } },
{ ".sym", { &parseDirectiveSym, 0 } },
{ ".relaximmsign", { &parseDirectiveRelaxImmSign, 0 } },

{ ".definelabel", { &parseDirectiveDefineLabel, 0 } },
{ ".definedatalabel", { &parseDirectiveDefineDataLabel, 0 } },
Expand Down
11 changes: 11 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,17 @@ By specifying `.nocash on`, No$gba semantics will be enabled for data directives

By specifying `.sym off`, any symbols (e.g. labels) defined after it will not be written to the symfile (if specified with the `-sym`/`-sym2` command line flag). This can be useful when using labels to define enum values that should not be interpreted as memory addresses. Writing to the symfile can be enabled again with `.sym on`. By default, this feature is on.

### Relaxed immediates sign

```
.relaximmsign on
.relaximmsign off
```

By default, armips checks whether immediate values in MIPS are in range based on the signedness of how the instruction interprets them. For example, for a hypothetical instruction with an 8-bit immediate, if the hardware interprets this immediate value as a signed number, armips will raise an error if it is not in the range -0x80 to 0x7F. Or if the hardware interprets this immediate value as an unsigned number, armips will raise an error if it is not in the range 0x00 to 0xFF.

By specifying `.relaximmsign on`, the range of valid immediates is relaxed to allow all values that would be valid with either signed or unsigned interpretation. In the example above, values of -0x80 to 0xFF would be allowed. This may be desirable for existing code which uses unsigned immediates as a shorthand for the hex representation of signed immediates, for example `addi r1, r1, 0xFFFF`.

## 5.2 MIPS directives

### Load delay
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.n64
.create "output.bin",0

.relaximmsign on
addiu $1, $1, 0xFFFF
.relaximmsign off
addiu $1, $1, 0xFFFF

.close
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Invalid After Turn Off.asm(7) error: Immediate value 0xFFFF out of range 0xFFFF8000 - 0x7FFF, .relaximmsign to suppress
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.n64
.create "output.bin",0

addiu $1, $1, 0x10000

.close
1 change: 1 addition & 0 deletions Tests/MIPS/RelaxImmSign/Invalid Always Above/expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Invalid Always Above.asm(4) error: Immediate value 0x10000 out of range 0xFFFF8000 - 0x7FFF
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.n64
.create "output.bin",0

addiu $1, $1, -0x8001

.close
1 change: 1 addition & 0 deletions Tests/MIPS/RelaxImmSign/Invalid Always Below/expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Invalid Always Below.asm(4) error: Immediate value 0xFFFF7FFF out of range 0xFFFF8000 - 0x7FFF
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.n64
.create "output.bin",0

addiu $1, $1, 0xFFFF

.close
1 change: 1 addition & 0 deletions Tests/MIPS/RelaxImmSign/Invalid Strict Above/expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Invalid Strict Above.asm(4) error: Immediate value 0xFFFF out of range 0xFFFF8000 - 0x7FFF, .relaximmsign to suppress
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.n64
.create "output.bin",0

sll $1, $1, -1

.close
1 change: 1 addition & 0 deletions Tests/MIPS/RelaxImmSign/Invalid Strict Below/expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Invalid Strict Below.asm(4) error: Immediate value 0xFFFFFFFF out of range 0x00 - 0x1F, .relaximmsign to suppress
15 changes: 15 additions & 0 deletions Tests/MIPS/RelaxImmSign/Valid/Valid.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.n64
.create "output.bin",0

addiu $1, $1, -0x8000
addiu $1, $1, 0x7FFF
andi $1, $1, 0xFFFF
sll $1, $1, 31

.relaximmsign on
addiu $1, $1, 0xFFFF
andi $1, $1, -1
sll $1, $1, -1
.relaximmsign off

.close
Binary file added Tests/MIPS/RelaxImmSign/Valid/expected.bin
Binary file not shown.