// DSP analyzer. // Can be used by disassembler as well as by interpreter/jitc. #pragma once namespace DSP { enum class DspInstruction { Unknown = -1, ABS, ///< 0xA100 ADD, ///< Adds accumulator $ac(1-D) to accumulator register $acD ADDARN, ///< Adds indexing register $ixS to an addressing register $arD ADDAX, ///< Adds secondary accumulator $axS to accumulator register $acD ADDAXL, ///< Adds secondary accumulator $axS.l to accumulator register $acD ADDI, ///< Adds a 16-bit sign-extended immediate to mid accumulator $acD.hm ADDIS, ///< Adds an 8-bit sign-extended immediate to mid accumulator $acD.hm ADDP, ///< Adds the product register to the accumulator register ADDPAXZ, ///< Adds secondary accumulator $axS to product register and stores result in accumulator register. Low 16-bits of $acD ($acD.l) are set to 0 ADDR, ///< Adds register $(0x18+S) to the accumulator $acD register ANDC, ///< Logic AND middle part of accumulator $acD.m with middle part of accumulator $ax(1-D).m. TCLR, ///< Test bit clear TSET, ///< Test bit set ANDI, ///< Logical AND with the mid part of accumulator $acD.m and the immediate value I ANDR, ///< Logical AND with the middle part of accumulator $acD.m and the high part of secondary accumulator, $axS.h ASL, ///< Arithmetically left shifts the accumulator $acR by the amount specified by immediate I ASR, ///< Arithmetically right shifts accumulator $acR specified by the value calculated by negating sign-extended bits 0-6 ASR16, ///< Arithmetically right shifts accumulator $acR by 16 BLOOP, ///< Block loop (Counter in register) BLOOPI, ///< Block loop (Counter in immediate operand) CALLcc, ///< Call function if condition cc has been met CALLR, ///< Call function (register) CLR, ///< Clears accumulator $acR CLRL, ///< Clears $acR.l - low 16 bits of accumulator $acR CLRP, ///< Clears product register $prod CMP, ///< Compares accumulator $ac0 with accumulator $ac1 CMPI, ///< Compares mid accumulator $acD.hm ($amD) with sign-extended immediate value I CMPIS, ///< Compares accumulator with short immediate CMPAR, ///< Compares accumulator $acS with accumulator axR.h. DAR, ///< Decrement address register $arD. DEC, ///< Decrements accumulator $acD. DECM, ///< Decrements 24-bit mid-accumulator $acsD HALT, ///< Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR IAR, ///< Increment address register $arD IFcc, ///< Executes the following opcode if the condition described by cccc has been met ILRR, ///< Move value from instruction memory pointed by addressing register $arS to mid accumulator register $acD.m ILRRD, ///< Move value from instruction memory pointed by addressing register $arS to mid accumulator register $acD.m. Decrement addressing register $arS ILRRI, ///< Move value from instruction memory pointed by addressing register $arS to mid accumulator register $acD.m. Increment addressing register $arS ILRRN, ///< Move value from instruction memory pointed by addressing register $arS to mid accumulator register $acD.m. Add corresponding indexing register $ixS to addressing register $arS INC, ///< Increments accumulator $acD INCM, ///< Increments 24-bit mid-accumulator $acsD Jcc, ///< Jumps to addressA if condition cc has been met JMPR, ///< Jump to address (by register) LOOP, ///< Loop (by register) LOOPI, ///< Loop (immediate operand) LR, ///< Move value from data memory pointed by address M to register $D. LRI, ///< Load immediate value I to register $D LRIS, ///< Load immediate value I (8-bit sign-extended) to accumulator register LRR, ///< Move value from data memory pointed by addressing register $S to register $D LRRD, ///< Move value from data memory pointed by addressing register $S to register $D. Decrements register $S LRRI, ///< Move value from data memory pointed by addressing register $S to register $D. Increments register $S LRRN, ///< Move value from data memory pointed by addressing register $S to register $D. Add indexing register $(0x4+S) to register $S LRS, ///< Move value from data memory pointed by address M (8-bit sign-extended) to register $(0x18+D) LSL, ///< Logically left shifts accumulator $acR by the amount specified by value I. LSL16, ///< Logically left shifts accumulator $acR by 16 LSR, ///< Logically right shifts accumulator $acR by the amount calculated by negating sign-extended bits 0–6 LSR16, ///< Logically right shifts accumulator $acR by 16 M2, ///< Clear SR_MUL_MODIFY M0, ///< Set SR_MUL_MODIFY CLR15, ///< Clear SR_MUL_UNSIGNED SET15, ///< Set SR_MUL_UNSIGNED CLR40, ///< Clear SR_40_MODE_BIT SET40, ///< Set SR_40_MODE_BIT MADD, ///< Multiply-add MADDC, MADDX, MOV, ///< Moves accumulator $ax(1-D) to accumulator $axD MOVAX, ///< Moves secondary accumulator $axS to accumulator $axD MOVNP, ///< Moves negated multiply product from the $prod register to the accumulator register $acD MOVP, ///< Moves multiply product from the $prod register to the accumulator register $acD MOVPZ, ///< Moves multiply product from the $prod register to the accumulator $acD and sets $acD.l to 0. MOVR, ///< Moves register $(0x18+S) (sign-extended) to middle accumulator $acD.hm. Sets $acD.l to 0. MRR, ///< Move value from register $S to register $D MSUB, ///< Multiply-sub MSUBC, MSUBX, MUL, ///< Multiply low part $axS.l of secondary accumulator $axS by high part $axS.h of secondary accumulator $axS (treat them both as signed). MULAC, MULC, MULCAC, ///< 3 operands MULCMV, ///< 3 operands MULCMVZ, ///< 3 operands MULMV, ///< 3 operands MULMVZ, ///< 3 operands MULX, ///< Multiply one part $ax0 by one part $ax1 (treat them both as signed). MULXAC, ///< 3 operands MULXMV, ///< 3 operands MULXMVZ, ///< 3 operands NEG, ///< Negates accumulator $acD. NOP, NX, ///< No operation, but can be extended with extended opcode ORC, ///< Logic OR middle part of accumulator $acD.m with middle part of accumulator $ax(1-D).m. ORI, ///< Logical OR of accumulator mid part $acD.m with immediate value I. ORR, ///< Logical OR middle part of accumulator $acD.m with high part of secondary accumulator $axS.h RETcc, ///< Return from subroutine if condition cc has been met RTI, ///< Return from exception SBSET, ///< Set bit of status register $sr. SBCLR, ///< Clear bit of status register $sr SI, ///< Store 16-bit immediate value I to a memory location pointed by address M SR, ///< Store value from register $S to a memory pointed by address M. SRR, ///< Store value from source register $S to a memory location pointed by addressing register $D SRRD, ///< Store value from source register $S to a memory location pointed by addressing register $D. Decrement register $D. SRRI, ///< Store value from source register $S to a memory location pointed by addressing register $D. Increment register $D SRRN, ///< Store value from source register $S to a memory location pointed by addressing register $D. Add indexing register $(0x4+D) to register $D SRS, ///< Store value from register $(0x18+S) to a memory pointed by address M (8-bit sign-extended). SUB, ///< Subtracts accumulator $ac(1-D) from accumulator register $acD SUBAX, ///< Subtracts secondary accumulator $axS from accumulator register $acD SUBP, ///< Subtracts product register from accumulator register SUBR, ///< Subtracts register $(0x18+S) from accumulator $acD register TST, ///< Test accumulator $acR TSTAXH, ///< Test hight part of secondary accumulator $axR.h. XORI, ///< Logical XOR (exclusive OR) of accumulator mid part $acD.m with immediate value I. XORR, ///< LogicalXOR(exclusiveOR)middlepartofaccumulator$acD.mwithhighpartofsecondaryaccumulator $axS.h. // Weird LSN, ///< Logically shifts right accumulator $ACC0 by lower 7-bit (signed) value in $AC1.M (if value negative, becomes left shift) ASN, ///< Arithmetically shifts right accumulator $ACC0 by lower 7-bit (signed) value in $AC1.M (if value negative, becomes left shift) // TODO: DIV (?) Max, }; ///< Extended opcodes // DSP instructions are in a hybrid format: some instructions occupy a full 16-bit word, and some can be packed as two 8-bit instructions per word. // Extended opcodes represents lower-part of instruction pair. enum class DspInstructionEx { Unknown = -1, NOP2, // 0x00 DR, // DR $arR IR, // IR $arR NR, // NR $arR, ixR MV, // MV $(0x18+D), $(0x1c+S) S, // S @$D, $(0x1c+D) SN, // SN @$D, $(0x1c+D) L, // L $(0x18+D), @$S LN, // LN $(0x18+D), @$S LS, // LS $(0x18+D), $acS.m SL, // SL $acS.m, $(0x18+D) LSN, // LSN $(0x18+D), $acS.m SLN, // SLN $acS.m, $(0x18+D) LSM, // LSM $(0x18+D), $acS.m SLM, // SLM $acS.m, $(0x18+D) LSNM, // LSNM $(0x18+D), $acS.m SLNM, // SLNM $acS.m, $(0x18+D) LD, // LD $ax0.d, $ax1.r, @$arS LDN, // LDN $ax0.d, $ax1.r, @$arS LDM, // LDM $ax0.d, $ax1.r, @$arS LDNM, // LDNM $ax0.d, $ax1.r, @$arS LDAX, // LDAX $axR, @$arS LDAXN, // LDAXN $axR, @$arS LDAXM, // LDAXM $axR, @$arS LDAXNM, // LDAXNM $axR, @$arS }; enum class DspParameter { Unknown = -1, // Registers ar0 = 0, ///< Addressing register 0 ar1, ///< Addressing register 1 ar2, ///< Addressing register 2 ar3, ///< Addressing register 3 ix0, ///< Indexing register 0 ix1, ///< Indexing register 1 ix2, ///< Indexing register 2 ix3, ///< Indexing register 3 lm0, ///< Limit register 0 lm1, ///< Limit register 1 lm2, ///< Limit register 2 lm3, ///< Limit register 3 st0, ///< Call stack register st1, ///< Data stack register st2, ///< Loop address stack register st3, ///< Loop counter register ac0h, ///< 40-bit Accumulator 0 (high) ac1h, ///< 40-bit Accumulator 1 (high) dpp, ///< Used as high 8-bits of address for some load/store instructions psr, ///< Processor Status register prodl, ///< Product register (low) prodm1, ///< Product register (mid 1) prodh, ///< Product register (high) prodm2, ///< Product register (mid 2) ax0l, ///< 32-bit Accumulator 0 (low) ax1l, ///< 32-bit Accumulator 1 (low) ax0h, ///< 32-bit Accumulator 0 (high) ax1h, ///< 32-bit Accumulator 1 (high) ac0l, ///< 40-bit Accumulator 0 (low) ac1l, ///< 40-bit Accumulator 1 (low) ac0m, ///< 40-bit Accumulator 0 (mid) ac1m, ///< 40-bit Accumulator 1 (mid) // Accumulator (40-bit) ac0, ac1, // Small accumulator (32-bit) ax0, ax1, // Indexed by register Indexed_regs, Indexed_ar0 = Indexed_regs, ///< @ Addressing register 0 Indexed_ar1, ///< @ Addressing register 1 Indexed_ar2, ///< @ Addressing register 2 Indexed_ar3, ///< @ Addressing register 3 Indexed_ix0, ///< @ Indexing register 0 Indexed_ix1, ///< @ Indexing register 1 Indexed_ix2, ///< @ Indexing register 2 Indexed_ix3, ///< @ Indexing register 3 // Immediates Byte, SignedByte, UnsignedShort, Address, Byte2, SignedByte2, UnsignedShort2, Address2, Max, }; enum class ConditionCode { GE = 0b0000, /// Greater than or equal LT = 0b0001, /// Less than GT = 0b0010, /// Greater than LE = 0b0011, /// Less than or equal NZ = 0b0100, /// Not equal Z = 0b0101, /// Equal NC = 0b0110, /// Not carry C = 0b0111, /// Carry NE = 0b1000, /// Below s32 E = 0b1001, /// Above s32 NM = 0b1010, /// Normalized M = 0b1011, /// Unnormalized NT = 0b1100, /// Bit Test Not OK T = 0b1101, /// Bit Test OK V = 0b1110, /// Overflow Always = 0b1111, /// Always }; #define DspAnalyzeNumParam 3 struct AnalyzeInfo { uint32_t clearingPaddy; ///< To use {0} on structure DspInstruction instr; ///< Processed instruction (ready to output) uint16_t instrBits; ///< Raw unprocessed opcode bits DspInstructionEx instrEx; ///< Processed extended opcode (ready to output) uint16_t instrExBits; ///< Raw unprocessed extended opcode bits bool extendedOpcodePresent; ///< Extended opcode present uint8_t bytes[0x10]; ///< Saved instruction bytes, including immediate operands size_t sizeInBytes; ///< Total size of instruction, including immediate/address operands (in bytes) size_t numParameters; ///< Number of instruction parameters (0-3) size_t numParametersEx; ///< Number of extended instruction parameters (1-2) DspParameter params[DspAnalyzeNumParam]; ///< Processed parameters (ready to output) uint16_t paramBits[DspAnalyzeNumParam]; ///< Raw unprocessed parameter bits (order MSB->LSB) DspParameter paramsEx[DspAnalyzeNumParam]; ///< Processed extended opcode parameters (ready to output) uint16_t paramExBits[DspAnalyzeNumParam]; ///< Raw unprocessed extended opcode parameter bits (order MSB->LSB) bool flowControl; ///< Branch, jump or another flow control instruction bool logic; ///< Or, And and similar simple logic operation (non-arithmetic) bool madd; ///< Heavy MADD/MSUB operation ///< Immediate/address operand, followed by instruction Word (or contained inside instruction) union { uint8_t Byte; int8_t SignedByte; uint16_t UnsignedShort; DspAddress Address; ///< For bloop, call etc. } ImmOperand; ///< Second immediate/address operand (required by small amount of instructions) union { uint8_t Byte; int8_t SignedByte; ///< For SI uint16_t UnsignedShort; DspAddress Address; ///< For BLOOPI } ImmOperand2; ConditionCode cc; ///< Some instructions has condition code }; class Analyzer { // Internal helpers static void ResetInfo(AnalyzeInfo& info); static bool Group0_Logic(uint8_t* instrPtr, size_t instrMaxSize, AnalyzeInfo& info, DspInstruction instr, bool logic); static bool Group0(uint8_t* instrPtr, size_t instrMaxSize, AnalyzeInfo& info); static bool Group1(uint8_t* instrPtr, size_t instrMaxSize, AnalyzeInfo& info); static bool Group2(AnalyzeInfo& info); static bool Group3(AnalyzeInfo& info); static bool Group4(AnalyzeInfo& info); static bool Group5(AnalyzeInfo& info); static bool Group6(AnalyzeInfo& info); static bool Group7(AnalyzeInfo& info); static bool Group8(AnalyzeInfo& info); static bool Group9(AnalyzeInfo& info); static bool GroupAB(AnalyzeInfo& info); static bool GroupCD(AnalyzeInfo& info); static bool GroupE(AnalyzeInfo& info); static bool GroupF(AnalyzeInfo& info); static bool GroupPacked(AnalyzeInfo& info); template static bool AddImmOperand(AnalyzeInfo& info, DspParameter param, T imm); static bool AddParam(AnalyzeInfo& info, DspParameter param, uint16_t paramBits); static bool AddParamEx(AnalyzeInfo& info, DspParameter param, uint16_t paramBits); // c++ commitete should try harder. Allowed only in headers.. static bool inline AddImmOperand(AnalyzeInfo& info, DspParameter param, uint8_t imm) { if (!AddParam(info, param, imm)) return false; if (param == DspParameter::Byte) info.ImmOperand.Byte = imm; else if (param == DspParameter::Byte2) info.ImmOperand2.Byte = imm; return true; } static bool inline AddImmOperand(AnalyzeInfo& info, DspParameter param, int8_t imm) { if (!AddParam(info, param, imm)) return false; if (param == DspParameter::SignedByte) info.ImmOperand.SignedByte = imm; else if (param == DspParameter::SignedByte2) info.ImmOperand2.SignedByte = imm; return true; } static bool inline AddImmOperand(AnalyzeInfo& info, DspParameter param, uint16_t imm) { if (!AddParam(info, param, imm)) return false; if (param == DspParameter::UnsignedShort) info.ImmOperand.UnsignedShort = imm; else if (param == DspParameter::UnsignedShort2) info.ImmOperand2.UnsignedShort = imm; return true; } static bool inline AddImmOperand(AnalyzeInfo& info, DspParameter param, DspAddress imm) { if (!AddParam(info, param, (uint16_t)imm)) return false; if (param == DspParameter::Address) info.ImmOperand.Address = imm; else if (param == DspParameter::Address2) info.ImmOperand2.Address = imm; return true; } static bool AddBytes(uint8_t* instrPtr, size_t bytes, AnalyzeInfo& info); public: static bool Analyze(uint8_t* instrPtr, size_t instrMaxSize, AnalyzeInfo& info); }; }