diff --git a/Archs/MIPS/CMipsInstruction.cpp b/Archs/MIPS/CMipsInstruction.cpp index 8b36b709..ac364a4c 100644 --- a/Archs/MIPS/CMipsInstruction.cpp +++ b/Archs/MIPS/CMipsInstruction.cpp @@ -6,6 +6,7 @@ #include "Core/Common.h" #include "Core/FileManager.h" #include "Core/Misc.h" +#include "Util/Util.h" CMipsInstruction::CMipsInstruction(MipsOpcodeData& opcode, MipsImmediateData& immediate, MipsRegisterData& registers) { @@ -47,39 +48,23 @@ int getImmediateBits(MipsImmediateType type) } } -// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/Allegrex/VfpuState.java?spec=svn3676&r=3383#1196 -int CMipsInstruction::floatToHalfFloat(int i) +bool CMipsInstruction::immediateToHalfFloat(int &out) { - int s = ((i >> 16) & 0x00008000); // sign - int e = ((i >> 23) & 0x000000ff) - (127 - 15); // exponent - int f = ((i >> 0) & 0x007fffff); // fraction - - // need to handle NaNs and Inf? - if (e <= 0) { - if (e < -10) { - if (s != 0) { - // handle -0.0 - return 0x8000; - } - return 0; - } - f = (f | 0x00800000) >> (1 - e); - return s | (f >> 13); - } else if (e == 0xff - (127 - 15)) { - if (f == 0) { - // Inf - return s | 0x7c00; - } - // NAN - return s | 0x7fff; + ExpressionValue value = immediateData.primary.expression.evaluate(); + if (value.isFloat()) + { + out = toHalfFloat(value.floatValue); + return true; } - - if (e > 30) { - // Overflow - return s | 0x7c00; + else if (value.isInt()) + { + out = toHalfFloat((double) value.intValue); + return true; + } + else + { + return false; } - - return s | (e << 10) | (f >> 13); } bool CMipsInstruction::Validate(const ValidateState &state) @@ -101,7 +86,18 @@ bool CMipsInstruction::Validate(const ValidateState &state) { if (immediateData.primary.expression.isLoaded()) { - if (!immediateData.primary.expression.evaluateInteger(immediateData.primary.value)) + bool isValidExpression; + + if (immediateData.primary.type == MipsImmediateType::ImmediateHalfFloat) + { + isValidExpression = immediateToHalfFloat(immediateData.primary.value); + } + else + { + isValidExpression = immediateData.primary.expression.evaluateInteger(immediateData.primary.value); + } + + if (!isValidExpression) { Logger::queueError(Logger::Error, "Invalid immediate expression"); return false; @@ -110,9 +106,6 @@ bool CMipsInstruction::Validate(const ValidateState &state) immediateData.primary.originalValue = immediateData.primary.value; } - if (immediateData.primary.type == MipsImmediateType::ImmediateHalfFloat) - immediateData.primary.value = floatToHalfFloat(immediateData.primary.originalValue); - if (opcodeData.opcode.flags & MO_IMMALIGNED) // immediate must be aligned { if (immediateData.primary.value % 4) diff --git a/Archs/MIPS/CMipsInstruction.h b/Archs/MIPS/CMipsInstruction.h index 13168d3e..0ed29c64 100644 --- a/Archs/MIPS/CMipsInstruction.h +++ b/Archs/MIPS/CMipsInstruction.h @@ -137,7 +137,7 @@ class CMipsInstruction: public CAssemblerCommand private: void encodeNormal() const; void encodeVfpu() const; - int floatToHalfFloat(int i); + bool immediateToHalfFloat(int &out); bool IgnoreLoadDelay; int64_t RamPos; diff --git a/Archs/MIPS/MipsOpcodes.cpp b/Archs/MIPS/MipsOpcodes.cpp index 7d531c57..08862cf5 100644 --- a/Archs/MIPS/MipsOpcodes.cpp +++ b/Archs/MIPS/MipsOpcodes.cpp @@ -603,7 +603,7 @@ const tMipsOpcode MipsOpcodes[] = { // hi |-------|-------|-------|-------|-------|-------|-------|-------| { "vadd.S", "vd,vs,vt", MIPS_VFPU0(0x00), MA_PSP, MO_VFPU }, { "vsub.S", "vd,vs,vt", MIPS_VFPU0(0x01), MA_PSP, MO_VFPU }, - { "vsbn.S", "vd,vs,vt", MIPS_VFPU0(0x02), MA_PSP, MO_VFPU }, + { "vsbn.s", "vd,vs,vt", MIPS_VFPU0(0x02), MA_PSP, MO_VFPU|MO_VFPU_SINGLE }, { "vdiv.S", "vd,vs,vt", MIPS_VFPU0(0x07), MA_PSP, MO_VFPU }, // 31-------26-----23----------------------------------------------0 @@ -614,7 +614,7 @@ const tMipsOpcode MipsOpcodes[] = { // |-------|-------|-------|-------|-------|-------|-------|-------| { "vmul.S", "vd,vs,vt", MIPS_VFPU1(0), MA_PSP, MO_VFPU }, { "vdot.S", "vd,vs,vt", MIPS_VFPU1(1), MA_PSP, MO_VFPU }, - { "vscl.S", "vd,vs,vt", MIPS_VFPU1(2), MA_PSP, MO_VFPU }, + { "vscl.S", "vd,vs,vSt", MIPS_VFPU1(2), MA_PSP, MO_VFPU }, { "vhdp.S", "vd,vs,vt", MIPS_VFPU1(4), MA_PSP, MO_VFPU }, { "vdet.S", "vd,vs,vt", MIPS_VFPU1(5), MA_PSP, MO_VFPU }, { "vcrs.S", "vd,vs,vt", MIPS_VFPU1(6), MA_PSP, MO_VFPU }, @@ -738,18 +738,18 @@ const tMipsOpcode MipsOpcodes[] = { { "vrndf1.S", "vd", MIPS_VFPU4_12(0x02), MA_PSP, MO_VFPU }, { "vrndf2.S", "vd", MIPS_VFPU4_12(0x03), MA_PSP, MO_VFPU }, // TODO: vsBZ? - { "vf2h.S", "vd,vs", MIPS_VFPU4_12(0x12), MA_PSP, MO_VFPU }, - { "vh2f.S", "vd,vs", MIPS_VFPU4_12(0x13), MA_PSP, MO_VFPU }, + { "vf2h.S", "vHd,vs", MIPS_VFPU4_12(0x12), MA_PSP, MO_VFPU }, + { "vh2f.S", "vDd,vs", MIPS_VFPU4_12(0x13), MA_PSP, MO_VFPU }, // TODO: vsBZ? { "vlgb.S", "vd,vs", MIPS_VFPU4_12(0x17), MA_PSP, MO_VFPU }, - { "vuc2i.S", "vd,vs", MIPS_VFPU4_12(0x18), MA_PSP, MO_VFPU }, - { "vc2i.S", "vd,vs", MIPS_VFPU4_12(0x19), MA_PSP, MO_VFPU }, - { "vus2i.S", "vd,vs", MIPS_VFPU4_12(0x1a), MA_PSP, MO_VFPU }, - { "vs2i.S", "vd,vs", MIPS_VFPU4_12(0x1b), MA_PSP, MO_VFPU }, - { "vi2uc.S", "vd,vs", MIPS_VFPU4_12(0x1c), MA_PSP, MO_VFPU }, - { "vi2c.S", "vd,vs", MIPS_VFPU4_12(0x1d), MA_PSP, MO_VFPU }, - { "vi2us.S", "vd,vs", MIPS_VFPU4_12(0x1e), MA_PSP, MO_VFPU }, - { "vi2s.S", "vd,vs", MIPS_VFPU4_12(0x1f), MA_PSP, MO_VFPU }, + { "vuc2ifs.S", "vQd,vs", MIPS_VFPU4_12(0x18), MA_PSP, MO_VFPU }, + { "vc2i.S", "vQd,vs", MIPS_VFPU4_12(0x19), MA_PSP, MO_VFPU }, + { "vus2i.S", "vDd,vs", MIPS_VFPU4_12(0x1a), MA_PSP, MO_VFPU }, + { "vs2i.S", "vDd,vs", MIPS_VFPU4_12(0x1b), MA_PSP, MO_VFPU }, + { "vi2uc.S", "vFd,vs", MIPS_VFPU4_12(0x1c), MA_PSP, MO_VFPU }, + { "vi2c.S", "vFd,vs", MIPS_VFPU4_12(0x1d), MA_PSP, MO_VFPU }, + { "vi2us.S", "vHd,vs", MIPS_VFPU4_12(0x1e), MA_PSP, MO_VFPU }, + { "vi2s.S", "vHd,vs", MIPS_VFPU4_12(0x1f), MA_PSP, MO_VFPU }, // 31--------------21------16--------------------------------------0 // |= VF4-1.3 | rt | | @@ -775,9 +775,9 @@ const tMipsOpcode MipsOpcodes[] = { { "vmtv.S", "vs,i7", MIPS_VFPU4_13(0x11)|0x00, MA_PSP, MO_VFPU }, { "vmfvc.S", "vs,i7", MIPS_VFPU4_13(0x10)|0x80, MA_PSP, MO_VFPU }, { "vmtvc.S", "vs,i7", MIPS_VFPU4_13(0x11)|0x80, MA_PSP, MO_VFPU }, - { "vt4444.S", "vd,vs", MIPS_VFPU4_13(0x19), MA_PSP, MO_VFPU }, - { "vt5551.S", "vd,vs", MIPS_VFPU4_13(0x1a), MA_PSP, MO_VFPU }, - { "vt5650.S", "vd,vs", MIPS_VFPU4_13(0x1b), MA_PSP, MO_VFPU }, + { "vt4444.S", "vHd,vs", MIPS_VFPU4_13(0x19), MA_PSP, MO_VFPU }, + { "vt5551.S", "vHd,vs", MIPS_VFPU4_13(0x1a), MA_PSP, MO_VFPU }, + { "vt5650.S", "vHd,vs", MIPS_VFPU4_13(0x1b), MA_PSP, MO_VFPU }, // 31-------26-----23----------------------------------------------0 // |= VFPU5| f | | diff --git a/Archs/MIPS/MipsParser.cpp b/Archs/MIPS/MipsParser.cpp index a2c15847..13a779c8 100644 --- a/Archs/MIPS/MipsParser.cpp +++ b/Archs/MIPS/MipsParser.cpp @@ -767,6 +767,15 @@ bool MipsParser::parseVfpuVrot(Parser& parser, int& result, int size) const Token& token = *tokenFinder; + if (token.type == TokenType::Integer) + { + if (token.getOriginalText() != "0" || isNeg) + { + return false; + } + continue; + } + if (token.type != TokenType::Identifier || token.identifierValue().size() != 1) return false; @@ -788,10 +797,6 @@ bool MipsParser::parseVfpuVrot(Parser& parser, int& result, int size) return false; cos = i; break; - case '0': - if (isNeg) - return false; - break; default: return false; } @@ -878,42 +883,42 @@ bool MipsParser::parseVpfxsParameter(Parser& parser, int& result) { // 0 sequenceParser.addEntry(0, {TokenType::Integer}, {INT64_C(0)} ); - // 1 - sequenceParser.addEntry(1, {TokenType::Integer}, {INT64_C(1)} ); - // 2 - sequenceParser.addEntry(2, {TokenType::Integer}, {INT64_C(2)} ); // 1/2 sequenceParser.addEntry(3, {TokenType::Integer, TokenType::Div, TokenType::Integer}, {INT64_C(1), INT64_C(2)} ); - // 3 - sequenceParser.addEntry(4, {TokenType::Integer}, {INT64_C(3)} ); // 1/3 sequenceParser.addEntry(5, {TokenType::Integer, TokenType::Div, TokenType::Integer}, {INT64_C(1), INT64_C(3)} ); // 1/4 sequenceParser.addEntry(6, {TokenType::Integer, TokenType::Div, TokenType::Integer}, {INT64_C(1), INT64_C(4)} ); // 1/6 sequenceParser.addEntry(7, {TokenType::Integer, TokenType::Div, TokenType::Integer}, {INT64_C(1), INT64_C(6)} ); + // 1 + sequenceParser.addEntry(1, {TokenType::Integer}, {INT64_C(1)} ); + // 2 + sequenceParser.addEntry(2, {TokenType::Integer}, {INT64_C(2)} ); + // 3 + sequenceParser.addEntry(4, {TokenType::Integer}, {INT64_C(3)} ); } - if (parser.nextToken().type != TokenType::LBrack) - return false; - + result = 0; + for (int i = 0; i < 4; i++) { - const Token *tokenFinder = &parser.nextToken(); + const Token *tokenFinder = &parser.peekToken(); if (i != 0) { if (tokenFinder->type != TokenType::Comma) return false; - - tokenFinder = &parser.nextToken(); + parser.eatToken(); + tokenFinder = &parser.peekToken(); } // negation if (tokenFinder->type == TokenType::Minus) { result |= 1 << (16+i); - tokenFinder = &parser.nextToken(); + parser.eatToken(); + tokenFinder = &parser.peekToken(); } // abs @@ -922,25 +927,32 @@ bool MipsParser::parseVpfxsParameter(Parser& parser, int& result) { result |= 1 << (8+i); abs = true; - tokenFinder = &parser.nextToken(); + parser.eatToken(); + tokenFinder = &parser.peekToken(); } const Token& token = *tokenFinder; - if (token.type != TokenType::Identifier) - return false; - - // check for register - const char* reg; - static const char* vpfxstRegisters = "xyzw"; - const std::string &stringValue = token.identifierValue().string(); - if (stringValue.size() == 1 && (reg = strchr(vpfxstRegisters,stringValue[0])) != nullptr) + if (token.type == TokenType::Identifier) { - result |= (reg-vpfxstRegisters) << (i*2); + parser.eatToken(); - if (abs && parser.nextToken().type != TokenType::BitOr) - return false; + // check for register + const char* reg; + static const char* vpfxstRegisters = "xyzw"; + const std::string &stringValue = token.identifierValue().string(); + if (stringValue.size() == 1 && (reg = strchr(vpfxstRegisters,stringValue[0])) != nullptr) + { + result |= (reg-vpfxstRegisters) << (i*2); - continue; + if (abs && parser.nextToken().type != TokenType::BitOr) + return false; + + continue; + } + else + { + return false; + } } // abs is invalid with constants @@ -958,7 +970,7 @@ bool MipsParser::parseVpfxsParameter(Parser& parser, int& result) result |= 1 << (8+i); } - return parser.nextToken().type == TokenType::RBrack; + return true; } bool MipsParser::parseVpfxdParameter(Parser& parser, int& result) @@ -968,67 +980,65 @@ bool MipsParser::parseVpfxdParameter(Parser& parser, int& result) // initialize on first use if (sequenceParser.getEntryCount() == 0) { - // 0-1 - sequenceParser.addEntry(1, - {TokenType::Integer, TokenType::Minus, TokenType::Integer}, - {INT64_C(0), INT64_C(1)} ); - // 0-1 + // m sequenceParser.addEntry(-1, - {TokenType::Integer, TokenType::Minus, TokenType::NumberString}, - {INT64_C(0), "1m"} ); - // 0:1 + {TokenType::Identifier}, + {"m"} ); + // [0:1] sequenceParser.addEntry(1, - {TokenType::Integer, TokenType::Colon, TokenType::Integer}, + {TokenType::LBrack, TokenType::Integer, TokenType::Colon, TokenType::Integer, TokenType::RBrack}, {INT64_C(0), INT64_C(1)} ); - // 0:1 - sequenceParser.addEntry(-1, - {TokenType::Integer, TokenType::Colon, TokenType::NumberString}, - {INT64_C(0), "1m"} ); - // -1-1 + // [-1:1] sequenceParser.addEntry(3, - {TokenType::Minus, TokenType::Integer, TokenType::Minus, TokenType::Integer}, + {TokenType::LBrack, TokenType::Minus, TokenType::Integer, TokenType::Colon, TokenType::Integer, TokenType::RBrack}, {INT64_C(1), INT64_C(1)} ); - // -1-1m - sequenceParser.addEntry(-3, - {TokenType::Minus, TokenType::Integer, TokenType::Minus, TokenType::NumberString}, - {INT64_C(1), "1m"} ); - // -1:1 - sequenceParser.addEntry(3, - {TokenType::Minus, TokenType::Integer, TokenType::Colon, TokenType::Integer}, - {INT64_C(1), INT64_C(1)} ); - // -1:1m - sequenceParser.addEntry(-3, - {TokenType::Minus, TokenType::Integer, TokenType::Colon, TokenType::NumberString}, - {INT64_C(1), "1m"} ); } + result = 0; + for (int i = 0; i < 4; i++) { + const Token *tokenFinder = &parser.peekToken(); + if (i != 0) { - if (parser.nextToken().type != TokenType::Comma) - return false; + CHECK(tokenFinder->type == TokenType::Comma); + parser.eatToken(); + tokenFinder = &parser.peekToken(); } - parser.eatToken(); - + + // empty + if (tokenFinder->type == TokenType::Comma) + { + CHECK(i < 3); + continue; + } + if (tokenFinder->type == TokenType::Separator) + { + CHECK(i == 3); + continue; + } + + // mask or saturation int num = 0; if (!sequenceParser.parse(parser,num)) return false; - // m versions if (num < 0) { + // mask result |= 1 << (8+i); - num = abs(num); } - - result |= num << (2*i); + else + { + // saturation + result |= num << (2*i); + } } - - return parser.nextToken().type == TokenType::RBrack; -} + return true; +} bool MipsParser::decodeCop2BranchCondition(const std::string& text, size_t& pos, int& result) { @@ -1252,10 +1262,11 @@ bool MipsParser::parseParameters(Parser& parser, const tMipsOpcode& opcode) // parse parameters MipsRegisterValue tempRegister; - int actualSize = opcodeData.vfpuSize; while (*encoding != 0) { + int actualSize = opcodeData.vfpuSize; + switch (*encoding++) { case 't': // register @@ -1283,11 +1294,53 @@ bool MipsParser::parseParameters(Parser& parser, const tMipsOpcode& opcode) CHECK(parseCop0Register(parser,registers.grd)); break; case 'v': // psp vfpu reg - if (*encoding == 'S') + if (*encoding == 'S') // Single { encoding++; actualSize = 0; } + else if (*encoding == 'H') // (one-)Half + { + encoding++; + switch (opcodeData.vfpuSize) + { + case 1: + actualSize = 0; + break; + case 3: + actualSize = 1; + break; + default: + return false; + } + } + else if (*encoding == 'D') // Double + { + encoding++; + switch (opcodeData.vfpuSize) + { + case 0: + actualSize = 1; + break; + case 1: + actualSize = 3; + break; + default: + return false; + } + } + else if (*encoding == 'F') // (one-)Fourth + { + encoding++; + CHECK(opcodeData.vfpuSize == 3); + actualSize = 0; + } + else if (*encoding == 'Q') // Quadruple + { + encoding++; + CHECK(opcodeData.vfpuSize == 0); + actualSize = 3; + } switch (*encoding++) { @@ -1628,7 +1681,7 @@ void MipsOpcodeFormatter::handleImmediate(MipsImmediateType type, unsigned int o switch (type) { case MipsImmediateType::ImmediateHalfFloat: - buffer += tfm::format("%f", bitsToFloat(originalValue)); + buffer += tfm::format("%f", fromHalfFloat(originalValue)); break; case MipsImmediateType::Immediate16: if (!(opcodeFlags & MO_IPCR) && originalValue & 0x8000) diff --git a/Readme.md b/Readme.md index 3cd7b329..0bee5374 100644 --- a/Readme.md +++ b/Readme.md @@ -1180,6 +1180,39 @@ will align the memory address to a multiple of 4, then create a label named `Mai ## 7.1 Change log +* Current Development Version + * *BREAKING* changes to PSP VFPU instruction parsing + * renamed `vuc2i.s` to [`vuc2ifs.s`](https://pspdev.github.io/vfpu-docs/#vuc2ifs.s) + * changed [`vfim.s`](https://pspdev.github.io/vfpu-docs/#vfim.s) half float immediate parsing from binary representation to float literals (was:`vfim.s S100,0x3f800000`, now: `vfim.s S100,1.0`) + * removed invalid instructions `vsbn.p`/`vsbn.t`/`vsbn.q` + * fixed `vt` register size for [`vscl.p`](https://pspdev.github.io/vfpu-docs/#vscl.p)/[`vscl.t`](https://pspdev.github.io/vfpu-docs/#vscl.q)/[`vscl.t`](https://pspdev.github.io/vfpu-docs/#vscl.q) (was: `vscl.t C100,C200,C203`, now: `vscl.t C100,C200,S203`) + * fixed `vd` register size for pack/unpack instructions (was: `vf2h.p C103,C202`, now: `vf2h.p S103,C202`) + * 2-to-1 packing instructions: + * [`vf2h.p`](https://pspdev.github.io/vfpu-docs/#vf2h.p)/[`vf2h.q`](https://pspdev.github.io/vfpu-docs/#vf2h.q): floats to packed half floats + * [`vi2us.p`](https://pspdev.github.io/vfpu-docs/#vi2us.p)/[`vi2us.q`](https://pspdev.github.io/vfpu-docs/#vi2us.q): integers to packed unsigned shorts (negatives clamp to 0; only keep 16 most significant bits) + * [`vi2s.p`](https://pspdev.github.io/vfpu-docs/#vi2s.p)/[`vi2s.q`](https://pspdev.github.io/vfpu-docs/#vi2s.q): integers to packed shorts (only keep 16 most significant bits) + * [`vt4444.q`](https://pspdev.github.io/vfpu-docs/#vt4444.q): four ABGR8888 color points to packed ABGR4444 + * [`vt5551.q`](https://pspdev.github.io/vfpu-docs/#vt5551.q): four ABGR8888 color points to packed ABGR1555 + * [`vt5650.q`](https://pspdev.github.io/vfpu-docs/#vt5650.q): four ABGR8888 color points to packed BGR565 + * 1-to-2 unpacking instructions: + * [`vh2f.s`](https://pspdev.github.io/vfpu-docs/#vh2f.s)/[`vh2f.p`](https://pspdev.github.io/vfpu-docs/#vh2f.p): packed half floats to floats + * [`vus2i.s`](https://pspdev.github.io/vfpu-docs/#vus2i.s)/[`vus2i.p`](https://pspdev.github.io/vfpu-docs/#vus2i.p): packed unsigned shorts to integers (least significant 15 bits set to 0) + * [`vs2i.s`](https://pspdev.github.io/vfpu-docs/#vs2i.s)/[`vs2i.p`](https://pspdev.github.io/vfpu-docs/#vs2i.p): packed shorts to integers (least significant 16 bits set to 0) + * 4-to-1 packing instructions: + * [`vi2uc.q`](https://pspdev.github.io/vfpu-docs/#vi2uc.q): integers to packed unsigned bytes (negatives clamp to 0; only keep 8 most significant bits) + * [`vi2c.q`](https://pspdev.github.io/vfpu-docs/#vi2c.q): integers to packed bytes (only keep 8 most significant bits) + * 1-to-4 unpacking instructions: + * [`vc2i.s`](https://pspdev.github.io/vfpu-docs/#vc2i.s): packed bytes to integers (least significant 24 bits set to 0) + * [`vuc2ifs.s`](https://pspdev.github.io/vfpu-docs/#vuc2ifs.s): packed unsigned bytes to integers (least significant 23 bits set to 0) + * updated `vpfxs`/`vpfxt`/`vpfxd` parsing to match [binutils](https://github.com/pspdev/binutils-gdb/blob/allegrex-v2.37.0/gas/config/tc-mips.c#L3442-L3538) & MWCC syntax + * changed saturation operations from `0-1`/`-1-1` & unbracketed `0:1`/`-1:1` to `[0:1]`/`[-1:1]` + * removed invalid saturation+mask operations `0-1m`/`-1-1m`/`0:1m`/`-1:1m` + * removed enclosing brackets from instruction operands (was: `vpfxs [x,y,z,w]`, now: `vpfxs x,y,z,w`) + * fixes to PSP VFPU instruction parsing + * fixed zero slots support for [`vrot.p`](https://pspdev.github.io/vfpu-docs/#vrot.p)/[`vrot.t`](https://pspdev.github.io/vfpu-docs/#vrot.t)/[`vrot.q`](https://pspdev.github.io/vfpu-docs/#vrot.q) + * fixed numeric constant operations for `vpfxs`/`vpfxt` (e.g. `vpfxs 1,1/2,1,0`) + * added write mask operation to `vpfxd` to skip writing a channel (e.g. `vpfxd ,,,m` prevents writing the last channel) + * added empty prefix to `vpfxd` to leave a channel unmodified (e.g. `vpfxd ,,,m` leaves the first three channels unmodified) * Version 0.11 * new `.aligna` directive for absolute address alignment * new expression functions: `org(label)`, `orga(label)`, `headersize(label)` diff --git a/Tests/MIPS/PSP Opcodes/PSP Opcodes.asm b/Tests/MIPS/PSP Opcodes/PSP Opcodes.asm index cabbb883..d75e4689 100644 --- a/Tests/MIPS/PSP Opcodes/PSP Opcodes.asm +++ b/Tests/MIPS/PSP Opcodes/PSP Opcodes.asm @@ -54,8 +54,62 @@ msubu a1,a2 ; VFPU0 vadd.s S100,S220,S333 vsub.p R122,C430,C010 -vsbn.t c121,C430,C010 -vdiv.q R122,C430,C010 - - -.close \ No newline at end of file +vcrsp.t c121,C430,C010 +vdiv.q C120,C430,C010 + + ; VFPU1 +vscl.t C100,C220,S333 + + ; VFPU3 +vcmp.s GT,S100,S101 + + ; VFPU5 +vpfxs x,-X,-|y|,w +vpfxt -3,1/4,1/6,0 +vpfxt 1/2,2,1,1/3 +vpfxd [0:1],m,[-1:1], +vpfxd ,[0:1],[-1:1],M + + ; typical representable half floats +vfim.s S123,0.0 +vfim.s S123,20.0 + ; 2^-14 is least representable half float +vfim.s S123,0.00006103515625 + ; 2^-15 would've been the least subnormal, but flushes to 0 +vfim.s S123,0.000030517578125 + ; smaller numbers all round to 0 +vfim.s S123,0.00001 + ; 2^15 - 2^5 is greatest representable half float +vfim.s S123,65504.0 + ; any number greater than that is nearer to that than infinity, so round down +vfim.s S123,1000000.0 + + ; VFPU4-1.2 +vf2h.p S102,C002 +vf2h.q C102,C200 +vh2f.s C102,S203 +vh2f.p C100,C202 +vc2i.s C100,S203 +vuc2ifs.s C100,S203 +vus2i.s C102,S203 +vus2i.p C100,C202 +vs2i.s C102,S203 +vs2i.p C100,C202 +vi2uc.q S103,C200 +vi2c.q S103,C200 +vi2us.p S103,C202 +vi2us.q C102,C200 +vi2s.p S103,C202 +vi2s.q C102,C200 + + ; VFPU4-1.3 +vt4444.q C102,C200 +vt5551.q C102,C200 +vt5650.q C102,C200 + + ; VF6-1.1 +vrot.p C102,S200,[C,-S] +vrot.t C101,S200,[C,0,-S] +vrot.q C100,S200,[0,C,-S,0] + +.close diff --git a/Tests/MIPS/PSP Opcodes/expected.bin b/Tests/MIPS/PSP Opcodes/expected.bin index c2a13515..c58d510c 100644 Binary files a/Tests/MIPS/PSP Opcodes/expected.bin and b/Tests/MIPS/PSP Opcodes/expected.bin differ diff --git a/Util/Util.cpp b/Util/Util.cpp index 7d7418f4..404a9879 100644 --- a/Util/Util.cpp +++ b/Util/Util.cpp @@ -109,6 +109,108 @@ int32_t getFloatBits(float value) return u.i; } +uint16_t toHalfFloat(double x) +{ + union + { + double value; + struct + { + unsigned long mantissa : 52; + unsigned int exponent : 11; + unsigned int sign : 1; + } parts; + }; + value = x; + + // per https://pspdev.github.io/vfpu-docs/#floating-point-format + // rounding is hardwired to nearest and subnormals flush to zero + if (parts.exponent == 0x7FF && parts.mantissa == 0) + { + // inf + return (parts.sign << 15) | (0x1F << 10); + } + else if (parts.exponent == 0x7FF && parts.mantissa != 0) + { + // NaN + return (parts.sign << 15) | (0x1F << 10) | 1; + } + else if (parts.exponent <= 0x3F0) + { + // 0, subnormals, numbers less than 2^-15 + return parts.sign << 15; + } + else + { + // numbers between 2^-14 (inclusive) and 2^16 (exclusive) + uint16_t exponent = parts.exponent - 0x3F0; + + // round to nearest (essentially `floor(x + 0.5)`) + uint64_t mantissa = ((parts.mantissa >> (42 - 1)) + 1) >> 1; + if (mantissa == 0x400) { + // round up to the next exponent + exponent += 1; + mantissa = 0; + } + if (exponent >= 0x1F) { + // round down to 65504 (greatest finite half float) + exponent = 0x1E; + mantissa = 0x3FF; + } + return (parts.sign << 15) | (exponent << 10) | mantissa; + } +} + +double fromHalfFloat(uint16_t x) +{ + using nl = std::numeric_limits; + union + { + uint16_t hValue; + struct + { + unsigned int mantissa : 10; + unsigned int exponent : 5; + unsigned int sign : 1; + } hParts; + }; + hValue = x; + + if (hParts.exponent == 0x1F && hParts.mantissa == 0) + { + // inf + return (hParts.sign ? -1.0 : 1.0) * nl::infinity(); + } + else if (hParts.exponent == 0x1F && hParts.mantissa != 0) + { + // NaN + return nl::signaling_NaN(); + } + else if (hParts.exponent == 0) + { + // 0 + return hParts.sign ? -0.0 : 0.0; + } + else + { + // numbers between 2^-14 (inclusive) and 2^16 (exclusive) + union + { + double dValue; + struct + { + unsigned long mantissa : 52; + unsigned int exponent : 11; + unsigned int sign : 1; + } dParts; + }; + dParts.mantissa = long(hParts.mantissa) << 42; + dParts.exponent = hParts.exponent + 0x3F0; + dParts.sign = hParts.sign; + return dValue; + } +} + float bitsToFloat(int32_t value) { union { float f; int32_t i; } u; diff --git a/Util/Util.h b/Util/Util.h index af77ed00..49d0ac2e 100644 --- a/Util/Util.h +++ b/Util/Util.h @@ -9,8 +9,9 @@ std::string convertWStringToUtf8(std::wstring_view source); bool stringToInt(const std::string& line, size_t start, size_t end, int64_t& result); int32_t getFloatBits(float value); -float bitsToFloat(int32_t value); int64_t getDoubleBits(double value); +uint16_t toHalfFloat(double value); +double fromHalfFloat(uint16_t value); std::string toLowercase(const std::string& str); size_t replaceAll(std::string& str, const char* oldValue,const std::string& newValue);