From cc8fc45c374694071a509182380c4dec4b11eb35 Mon Sep 17 00:00:00 2001 From: Sauraen Date: Sat, 20 Sep 2025 16:54:35 -0700 Subject: [PATCH 1/3] Initial implementation --- .gitignore | 3 +++ Archs/MIPS/CMipsInstruction.cpp | 21 +++++++++++++++------ Archs/MIPS/CMipsInstruction.h | 1 + Archs/MIPS/Mips.cpp | 1 + Archs/MIPS/Mips.h | 3 +++ Archs/MIPS/MipsOpcodes.cpp | 18 +++++++++--------- Archs/MIPS/MipsOpcodes.h | 1 + 7 files changed, 33 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 804a7e75..3d5d9ff5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ # Visual Studio CMake directories. /.vs/ /out/ + +# Non-Windows CMake directory. +/build/ diff --git a/Archs/MIPS/CMipsInstruction.cpp b/Archs/MIPS/CMipsInstruction.cpp index 8b36b709..0fd87a0c 100644 --- a/Archs/MIPS/CMipsInstruction.cpp +++ b/Archs/MIPS/CMipsInstruction.cpp @@ -15,6 +15,7 @@ CMipsInstruction::CMipsInstruction(MipsOpcodeData& opcode, MipsImmediateData& im addNop = false; IgnoreLoadDelay = Mips.GetIgnoreDelay(); + RelaxImmediateSign = Mips.RelaxImmediateSign(); } CMipsInstruction::~CMipsInstruction() @@ -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; @@ -156,16 +157,24 @@ 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) + // Example: 8 bit immediate. If signed, -0x80 to 0x7F. If unsigned, 0x00 to 0xFF. If relaxed, -0x80 to 0xFF. + int maxInclusive = ((opcodeData.opcode.flags & MO_IMMUNSIGNED) || RelaxImmediateSign) + ? (1 << immediateBits) - 1 + : (1 << (immediateBits - 1)) - 1; + int minInclusive = (!(opcodeData.opcode.flags & MO_IMMUNSIGNED) || RelaxImmediateSign) + ? -(1 << (immediateBits - 1)) + : 0; + if (immediateData.primary.value < minInclusive || immediateData.primary.value > maxInclusive) { - Logger::queueError(Logger::Error, "Immediate value 0x%0*X out of range",digits,immediateData.primary.value); + Logger::queueError(Logger::Error, "Immediate value 0x%0*X out of range 0x%0*X - 0x%0*X", + digits, immediateData.primary.value, + digits, minInclusive, + digits, maxInclusive); return false; } - immediateData.primary.value &= mask; + immediateData.primary.value &= (1 << immediateBits) - 1; } if (immediateData.secondary.type != MipsImmediateType::None) diff --git a/Archs/MIPS/CMipsInstruction.h b/Archs/MIPS/CMipsInstruction.h index 13168d3e..96395c4d 100644 --- a/Archs/MIPS/CMipsInstruction.h +++ b/Archs/MIPS/CMipsInstruction.h @@ -140,6 +140,7 @@ class CMipsInstruction: public CAssemblerCommand int floatToHalfFloat(int i); bool IgnoreLoadDelay; + bool RelaxImmediateSign; int64_t RamPos; bool addNop; diff --git a/Archs/MIPS/Mips.cpp b/Archs/MIPS/Mips.cpp index 38ab5f1b..049fc787 100644 --- a/Archs/MIPS/Mips.cpp +++ b/Archs/MIPS/Mips.cpp @@ -13,6 +13,7 @@ CMipsArchitecture::CMipsArchitecture() LoadDelay = false; LoadDelayRegister = 0; DelaySlot = false; + RelaxImmediateSign = false; Version = MARCH_INVALID; } diff --git a/Archs/MIPS/Mips.h b/Archs/MIPS/Mips.h index 5de8073c..fa245ae8 100644 --- a/Archs/MIPS/Mips.h +++ b/Archs/MIPS/Mips.h @@ -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; }; diff --git a/Archs/MIPS/MipsOpcodes.cpp b/Archs/MIPS/MipsOpcodes.cpp index 7d531c57..d135017d 100644 --- a/Archs/MIPS/MipsOpcodes.cpp +++ b/Archs/MIPS/MipsOpcodes.cpp @@ -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 }, @@ -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 }, diff --git a/Archs/MIPS/MipsOpcodes.h b/Archs/MIPS/MipsOpcodes.h index 9932b29a..8c89f985 100644 --- a/Archs/MIPS/MipsOpcodes.h +++ b/Archs/MIPS/MipsOpcodes.h @@ -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 From 35dc773e9b98d6213a5a48e91d6e1710f7397ab7 Mon Sep 17 00:00:00 2001 From: Sauraen Date: Sat, 20 Sep 2025 17:11:07 -0700 Subject: [PATCH 2/3] Made shift instruction immediates unsigned --- Archs/MIPS/CMipsInstruction.cpp | 2 +- Archs/MIPS/MipsOpcodes.cpp | 40 ++++++++++++++++----------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Archs/MIPS/CMipsInstruction.cpp b/Archs/MIPS/CMipsInstruction.cpp index 0fd87a0c..feb310e7 100644 --- a/Archs/MIPS/CMipsInstruction.cpp +++ b/Archs/MIPS/CMipsInstruction.cpp @@ -15,7 +15,7 @@ CMipsInstruction::CMipsInstruction(MipsOpcodeData& opcode, MipsImmediateData& im addNop = false; IgnoreLoadDelay = Mips.GetIgnoreDelay(); - RelaxImmediateSign = Mips.RelaxImmediateSign(); + RelaxImmediateSign = Mips.GetRelaxImmediateSign(); } CMipsInstruction::~CMipsInstruction() diff --git a/Archs/MIPS/MipsOpcodes.cpp b/Archs/MIPS/MipsOpcodes.cpp index d135017d..da705f54 100644 --- a/Archs/MIPS/MipsOpcodes.cpp +++ b/Archs/MIPS/MipsOpcodes.cpp @@ -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 }, @@ -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 From cfa4f8b6ad30216c13ee49b015005dcb33840b79 Mon Sep 17 00:00:00 2001 From: Sauraen Date: Sun, 28 Sep 2025 16:06:03 -0700 Subject: [PATCH 3/3] Added directive RelaxImmSign and tests --- Archs/MIPS/CMipsInstruction.cpp | 54 +++++++++++++----- Parser/DirectivesParser.cpp | 25 ++++++++ Readme.md | 11 ++++ .../Invalid After Turn Off.asm | 9 +++ .../Invalid After Turn Off/expected.txt | 1 + .../Invalid Always Above.asm | 6 ++ .../Invalid Always Above/expected.txt | 1 + .../Invalid Always Below.asm | 6 ++ .../Invalid Always Below/expected.txt | 1 + .../Invalid Strict Above.asm | 6 ++ .../Invalid Strict Above/expected.txt | 1 + .../Invalid Strict Below.asm | 6 ++ .../Invalid Strict Below/expected.txt | 1 + Tests/MIPS/RelaxImmSign/Valid/Valid.asm | 15 +++++ Tests/MIPS/RelaxImmSign/Valid/expected.bin | Bin 0 -> 28 bytes 15 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 Tests/MIPS/RelaxImmSign/Invalid After Turn Off/Invalid After Turn Off.asm create mode 100644 Tests/MIPS/RelaxImmSign/Invalid After Turn Off/expected.txt create mode 100644 Tests/MIPS/RelaxImmSign/Invalid Always Above/Invalid Always Above.asm create mode 100644 Tests/MIPS/RelaxImmSign/Invalid Always Above/expected.txt create mode 100644 Tests/MIPS/RelaxImmSign/Invalid Always Below/Invalid Always Below.asm create mode 100644 Tests/MIPS/RelaxImmSign/Invalid Always Below/expected.txt create mode 100644 Tests/MIPS/RelaxImmSign/Invalid Strict Above/Invalid Strict Above.asm create mode 100644 Tests/MIPS/RelaxImmSign/Invalid Strict Above/expected.txt create mode 100644 Tests/MIPS/RelaxImmSign/Invalid Strict Below/Invalid Strict Below.asm create mode 100644 Tests/MIPS/RelaxImmSign/Invalid Strict Below/expected.txt create mode 100644 Tests/MIPS/RelaxImmSign/Valid/Valid.asm create mode 100644 Tests/MIPS/RelaxImmSign/Valid/expected.bin diff --git a/Archs/MIPS/CMipsInstruction.cpp b/Archs/MIPS/CMipsInstruction.cpp index feb310e7..d35313ab 100644 --- a/Archs/MIPS/CMipsInstruction.cpp +++ b/Archs/MIPS/CMipsInstruction.cpp @@ -158,23 +158,49 @@ bool CMipsInstruction::Validate(const ValidateState &state) int immediateBits = getImmediateBits(immediateData.primary.type); int digits = (immediateBits+3) / 4; + // Example: 8 bit immediate. If signed, -0x80 to 0x7F. If unsigned, 0x00 to 0xFF. If relaxed, -0x80 to 0xFF. - int maxInclusive = ((opcodeData.opcode.flags & MO_IMMUNSIGNED) || RelaxImmediateSign) - ? (1 << immediateBits) - 1 - : (1 << (immediateBits - 1)) - 1; - int minInclusive = (!(opcodeData.opcode.flags & MO_IMMUNSIGNED) || RelaxImmediateSign) - ? -(1 << (immediateBits - 1)) - : 0; - if (immediateData.primary.value < minInclusive || immediateData.primary.value > maxInclusive) - { - Logger::queueError(Logger::Error, "Immediate value 0x%0*X out of range 0x%0*X - 0x%0*X", - digits, immediateData.primary.value, - digits, minInclusive, - digits, maxInclusive); - return false; + 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 &= (1 << immediateBits) - 1; + immediateData.primary.value &= maxInclusiveUns; } if (immediateData.secondary.type != MipsImmediateType::None) diff --git a/Parser/DirectivesParser.cpp b/Parser/DirectivesParser.cpp index d20d401f..11bf1baa 100644 --- a/Parser/DirectivesParser.cpp +++ b/Parser/DirectivesParser.cpp @@ -619,6 +619,30 @@ std::unique_ptr parseDirectiveSym(Parser& parser, int flags) return nullptr; } +std::unique_ptr parseDirectiveRelaxImmSign(Parser& parser, int flags) +{ + auto stringValue = getStringOrIdentifier(parser); + if (!stringValue) + return nullptr; + + Architecture& arch = Architecture::current(); + CMipsArchitecture* mipsArch = dynamic_cast(&arch); + if (!mipsArch) + return nullptr; + + if (stringValue == "on") + { + mipsArch->SetRelaxImmediateSign(true); + return std::make_unique(); + } else if (stringValue == "off") + { + mipsArch->SetRelaxImmediateSign(false); + return std::make_unique(); + } + + return nullptr; +} + std::unique_ptr parseDirectiveDefineLabel(Parser& parser, int flags, std::optional thumbMode) { const Token& tok = parser.nextToken(); @@ -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 } }, diff --git a/Readme.md b/Readme.md index 3cd7b329..bfffae34 100644 --- a/Readme.md +++ b/Readme.md @@ -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 diff --git a/Tests/MIPS/RelaxImmSign/Invalid After Turn Off/Invalid After Turn Off.asm b/Tests/MIPS/RelaxImmSign/Invalid After Turn Off/Invalid After Turn Off.asm new file mode 100644 index 00000000..929c611a --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid After Turn Off/Invalid After Turn Off.asm @@ -0,0 +1,9 @@ +.n64 +.create "output.bin",0 + +.relaximmsign on +addiu $1, $1, 0xFFFF +.relaximmsign off +addiu $1, $1, 0xFFFF + +.close diff --git a/Tests/MIPS/RelaxImmSign/Invalid After Turn Off/expected.txt b/Tests/MIPS/RelaxImmSign/Invalid After Turn Off/expected.txt new file mode 100644 index 00000000..87fbe166 --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid After Turn Off/expected.txt @@ -0,0 +1 @@ +Invalid After Turn Off.asm(7) error: Immediate value 0xFFFF out of range 0xFFFF8000 - 0x7FFF, .relaximmsign to suppress diff --git a/Tests/MIPS/RelaxImmSign/Invalid Always Above/Invalid Always Above.asm b/Tests/MIPS/RelaxImmSign/Invalid Always Above/Invalid Always Above.asm new file mode 100644 index 00000000..e14d6586 --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid Always Above/Invalid Always Above.asm @@ -0,0 +1,6 @@ +.n64 +.create "output.bin",0 + +addiu $1, $1, 0x10000 + +.close diff --git a/Tests/MIPS/RelaxImmSign/Invalid Always Above/expected.txt b/Tests/MIPS/RelaxImmSign/Invalid Always Above/expected.txt new file mode 100644 index 00000000..c8bd40d8 --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid Always Above/expected.txt @@ -0,0 +1 @@ +Invalid Always Above.asm(4) error: Immediate value 0x10000 out of range 0xFFFF8000 - 0x7FFF diff --git a/Tests/MIPS/RelaxImmSign/Invalid Always Below/Invalid Always Below.asm b/Tests/MIPS/RelaxImmSign/Invalid Always Below/Invalid Always Below.asm new file mode 100644 index 00000000..93425006 --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid Always Below/Invalid Always Below.asm @@ -0,0 +1,6 @@ +.n64 +.create "output.bin",0 + +addiu $1, $1, -0x8001 + +.close diff --git a/Tests/MIPS/RelaxImmSign/Invalid Always Below/expected.txt b/Tests/MIPS/RelaxImmSign/Invalid Always Below/expected.txt new file mode 100644 index 00000000..992fe7b9 --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid Always Below/expected.txt @@ -0,0 +1 @@ +Invalid Always Below.asm(4) error: Immediate value 0xFFFF7FFF out of range 0xFFFF8000 - 0x7FFF diff --git a/Tests/MIPS/RelaxImmSign/Invalid Strict Above/Invalid Strict Above.asm b/Tests/MIPS/RelaxImmSign/Invalid Strict Above/Invalid Strict Above.asm new file mode 100644 index 00000000..2f47feb5 --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid Strict Above/Invalid Strict Above.asm @@ -0,0 +1,6 @@ +.n64 +.create "output.bin",0 + +addiu $1, $1, 0xFFFF + +.close diff --git a/Tests/MIPS/RelaxImmSign/Invalid Strict Above/expected.txt b/Tests/MIPS/RelaxImmSign/Invalid Strict Above/expected.txt new file mode 100644 index 00000000..37540721 --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid Strict Above/expected.txt @@ -0,0 +1 @@ +Invalid Strict Above.asm(4) error: Immediate value 0xFFFF out of range 0xFFFF8000 - 0x7FFF, .relaximmsign to suppress diff --git a/Tests/MIPS/RelaxImmSign/Invalid Strict Below/Invalid Strict Below.asm b/Tests/MIPS/RelaxImmSign/Invalid Strict Below/Invalid Strict Below.asm new file mode 100644 index 00000000..45357133 --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid Strict Below/Invalid Strict Below.asm @@ -0,0 +1,6 @@ +.n64 +.create "output.bin",0 + +sll $1, $1, -1 + +.close diff --git a/Tests/MIPS/RelaxImmSign/Invalid Strict Below/expected.txt b/Tests/MIPS/RelaxImmSign/Invalid Strict Below/expected.txt new file mode 100644 index 00000000..dd6564bc --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Invalid Strict Below/expected.txt @@ -0,0 +1 @@ +Invalid Strict Below.asm(4) error: Immediate value 0xFFFFFFFF out of range 0x00 - 0x1F, .relaximmsign to suppress diff --git a/Tests/MIPS/RelaxImmSign/Valid/Valid.asm b/Tests/MIPS/RelaxImmSign/Valid/Valid.asm new file mode 100644 index 00000000..c51d7c20 --- /dev/null +++ b/Tests/MIPS/RelaxImmSign/Valid/Valid.asm @@ -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 diff --git a/Tests/MIPS/RelaxImmSign/Valid/expected.bin b/Tests/MIPS/RelaxImmSign/Valid/expected.bin new file mode 100644 index 0000000000000000000000000000000000000000..ae796f8a4e5bc37425cef4fcc1e90594fcd811e7 GIT binary patch literal 28 ccmY#UY+z7Ptp9JI`2RlxBmV&vAPr*!0FtQ-3IG5A literal 0 HcmV?d00001