// // Decoder.cpp // Clock Signal // // Created by Thomas Harte on 12/30/20. // Copyright © 2020 Thomas Harte. All rights reserved. // #include "Decoder.hpp" using namespace InstructionSet::PowerPC; Decoder::Decoder(Model model) : model_(model) {} Instruction Decoder::decode(uint32_t opcode) { // Quick bluffer's guide to PowerPC instruction encoding: // // There is a six-bit field at the very top of the instruction. // Sometimes that fully identifies an instruction, but usually // it doesn't. // // There is an addition 9- or 10-bit field starting one bit above // least significant that disambiguates the rest. Strictly speaking // it's a 10-bit field, but the mnemonics for many instructions treat // it as a 9-bit field with a flag at the top. // // I've decided to hew directly to the mnemonics. // // Various opcodes in the 1995 documentation define reserved bits, // which are given the nominal value of 0. It does not give a formal // definition of a reserved bit. As a result this code does not // currently check the value of reserved bits. That may need to change // if/when I add support for extended instruction sets. #define Bind(mask, operation) case mask: return Instruction(Operation::operation, opcode); #define BindSupervisor(mask, operation) case mask: return Instruction(Operation::operation, opcode, true); #define BindConditional(condition, mask, operation) \ case mask: \ if(condition()) return Instruction(Operation::operation, opcode); \ return Instruction(opcode); #define BindSupervisorConditional(condition, mask, operation) \ case mask: \ if(condition()) return Instruction(Operation::operation, opcode, true); \ return Instruction(opcode); #define Six(x) (unsigned(x) << 26) #define SixTen(x, y) (Six(x) | ((y) << 1)) // First pass: weed out all those instructions identified entirely by the // top six bits. switch(opcode & Six(0b111111)) { default: break; BindConditional(is64bit, Six(0b000010), tdi); Bind(Six(0b000011), twi); Bind(Six(0b000111), mulli); Bind(Six(0b001000), subfic); Bind(Six(0b001100), addic); Bind(Six(0b001101), addic_); Bind(Six(0b001110), addi); Bind(Six(0b001111), addis); case Six(0b010000): { // This might be a bcx, but check for a valid bo field. switch((opcode >> 21) & 0x1f) { case 0: case 1: case 2: case 3: case 4: case 5: case 8: case 9: case 10: case 11: case 12: case 13: case 16: case 17: case 18: case 19: case 20: return Instruction(Operation::bcx, opcode); default: return Instruction(opcode); } } break; Bind(Six(0b010010), bx); Bind(Six(0b010100), rlwimix); Bind(Six(0b010101), rlwinmx); Bind(Six(0b010111), rlwnmx); Bind(Six(0b011000), ori); Bind(Six(0b011001), oris); Bind(Six(0b011010), xori); Bind(Six(0b011011), xoris); Bind(Six(0b011100), andi_); Bind(Six(0b011101), andis_); Bind(Six(0b100000), lwz); Bind(Six(0b100001), lwzu); Bind(Six(0b100010), lbz); Bind(Six(0b100011), lbzu); Bind(Six(0b100100), stw); Bind(Six(0b100101), stwu); Bind(Six(0b100110), stb); Bind(Six(0b100111), stbu); Bind(Six(0b101000), lhz); Bind(Six(0b101001), lhzu); Bind(Six(0b101010), lha); Bind(Six(0b101011), lhau); Bind(Six(0b101100), sth); Bind(Six(0b101101), sthu); Bind(Six(0b101110), lmw); Bind(Six(0b101111), stmw); Bind(Six(0b110000), lfs); Bind(Six(0b110001), lfsu); Bind(Six(0b110010), lfd); Bind(Six(0b110011), lfdu); Bind(Six(0b110100), stfs); Bind(Six(0b110101), stfsu); Bind(Six(0b110110), stfd); Bind(Six(0b110111), stfdu); BindConditional(is601, Six(9), dozi); BindConditional(is601, Six(22), rlmix); Bind(Six(0b001010), cmpli); Bind(Six(0b001011), cmpi); } // Second pass: all those with a top six bits and a bottom nine or ten. switch(opcode & SixTen(0b111111, 0b1111111111)) { default: break; // 64-bit instructions. BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux); BindConditional(is64bit, SixTen(0b011111, 0b1000001001), mulhdux); BindConditional(is64bit, SixTen(0b011111, 0b0000010101), ldx); BindConditional(is64bit, SixTen(0b011111, 0b0000011011), sldx); BindConditional(is64bit, SixTen(0b011111, 0b0000110101), ldux); BindConditional(is64bit, SixTen(0b011111, 0b0000111010), cntlzdx); BindConditional(is64bit, SixTen(0b011111, 0b0001000100), td); BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx); BindConditional(is64bit, SixTen(0b011111, 0b1001001001), mulhdx); BindConditional(is64bit, SixTen(0b011111, 0b0001010100), ldarx); BindConditional(is64bit, SixTen(0b011111, 0b0010010101), stdx); BindConditional(is64bit, SixTen(0b011111, 0b0010110101), stdux); BindConditional(is64bit, SixTen(0b011111, 0b0011101001), mulld); BindConditional(is64bit, SixTen(0b011111, 0b1011101001), mulld); BindConditional(is64bit, SixTen(0b011111, 0b0101010101), lwax); BindConditional(is64bit, SixTen(0b011111, 0b0101110101), lwaux); BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix); BindConditional(is64bit, SixTen(0b011111, 0b1100111010), sradix); BindConditional(is64bit, SixTen(0b011111, 0b0110110010), slbie); BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux); BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux); BindConditional(is64bit, SixTen(0b011111, 0b0111101001), divdx); BindConditional(is64bit, SixTen(0b011111, 0b1111101001), divdx); BindConditional(is64bit, SixTen(0b011111, 0b1000011011), srdx); BindConditional(is64bit, SixTen(0b011111, 0b1100011010), sradx); BindConditional(is64bit, SixTen(0b111111, 0b1111011010), extsw); // Power instructions; these are all taken from the MPC601 manual rather than // the PowerPC Programmer's Reference Guide, hence the decimal encoding of the // ten-bit field. BindConditional(is601, SixTen(0b011111, 360), absx); BindConditional(is601, SixTen(0b011111, 512 + 360), absx); BindConditional(is601, SixTen(0b011111, 531), clcs); BindConditional(is601, SixTen(0b011111, 331), divx); BindConditional(is601, SixTen(0b011111, 512 + 331), divx); BindConditional(is601, SixTen(0b011111, 363), divsx); BindConditional(is601, SixTen(0b011111, 512 + 363), divsx); BindConditional(is601, SixTen(0b011111, 264), dozx); BindConditional(is601, SixTen(0b011111, 512 + 264), dozx); BindConditional(is601, SixTen(0b011111, 277), lscbxx); BindConditional(is601, SixTen(0b011111, 29), maskgx); BindConditional(is601, SixTen(0b011111, 541), maskirx); BindConditional(is601, SixTen(0b011111, 107), mulx); BindConditional(is601, SixTen(0b011111, 512 + 107), mulx); BindConditional(is601, SixTen(0b011111, 488), nabsx); BindConditional(is601, SixTen(0b011111, 512 + 488), nabsx); BindConditional(is601, SixTen(0b011111, 537), rribx); BindConditional(is601, SixTen(0b011111, 153), slex); BindConditional(is601, SixTen(0b011111, 217), sleqx); BindConditional(is601, SixTen(0b011111, 184), sliqx); BindConditional(is601, SixTen(0b011111, 248), slliqx); BindConditional(is601, SixTen(0b011111, 216), sllqx); BindConditional(is601, SixTen(0b011111, 152), slqx); BindConditional(is601, SixTen(0b011111, 952), sraiqx); BindConditional(is601, SixTen(0b011111, 920), sraqx); BindConditional(is601, SixTen(0b011111, 665), srex); BindConditional(is601, SixTen(0b011111, 921), sreax); BindConditional(is601, SixTen(0b011111, 729), sreqx); BindConditional(is601, SixTen(0b011111, 696), sriqx); BindConditional(is601, SixTen(0b011111, 760), srliqx); BindConditional(is601, SixTen(0b011111, 728), srlqx); BindConditional(is601, SixTen(0b011111, 664), srqx); // 32-bit instructions. Bind(SixTen(0b010011, 0b0000000000), mcrf); Bind(SixTen(0b010011, 0b0000010000), bclrx); Bind(SixTen(0b010011, 0b0000100001), crnor); Bind(SixTen(0b010011, 0b0000110010), rfi); Bind(SixTen(0b010011, 0b0010000001), crandc); Bind(SixTen(0b010011, 0b0010010110), isync); Bind(SixTen(0b010011, 0b0011000001), crxor); Bind(SixTen(0b010011, 0b0011100001), crnand); Bind(SixTen(0b010011, 0b0100000001), crand); Bind(SixTen(0b010011, 0b0100100001), creqv); Bind(SixTen(0b010011, 0b0110100001), crorc); Bind(SixTen(0b010011, 0b0111000001), cror); Bind(SixTen(0b010011, 0b1000010000), bcctrx); Bind(SixTen(0b011111, 0b0000000000), cmp); Bind(SixTen(0b011111, 0b0000000100), tw); Bind(SixTen(0b011111, 0b0000001000), subfcx); Bind(SixTen(0b011111, 0b1000001000), subfcx); Bind(SixTen(0b011111, 0b0000001010), addcx); Bind(SixTen(0b011111, 0b1000001010), addcx); Bind(SixTen(0b011111, 0b0000001011), mulhwux); Bind(SixTen(0b011111, 0b1000001011), mulhwux); Bind(SixTen(0b011111, 0b0000010011), mfcr); Bind(SixTen(0b011111, 0b0000010100), lwarx); Bind(SixTen(0b011111, 0b0000010111), lwzx); Bind(SixTen(0b011111, 0b0000011000), slwx); Bind(SixTen(0b011111, 0b0000011010), cntlzwx); Bind(SixTen(0b011111, 0b0000011100), andx); Bind(SixTen(0b011111, 0b0000100000), cmpl); Bind(SixTen(0b011111, 0b0000101000), subfx); Bind(SixTen(0b011111, 0b1000101000), subfx); Bind(SixTen(0b011111, 0b0000110110), dcbst); Bind(SixTen(0b011111, 0b0000110111), lwzux); Bind(SixTen(0b011111, 0b0000111100), andcx); Bind(SixTen(0b011111, 0b0001001011), mulhwx); Bind(SixTen(0b011111, 0b1001001011), mulhwx); Bind(SixTen(0b011111, 0b0001010011), mfmsr); Bind(SixTen(0b011111, 0b0001010110), dcbf); Bind(SixTen(0b011111, 0b0001010111), lbzx); Bind(SixTen(0b011111, 0b0001101000), negx); Bind(SixTen(0b011111, 0b1001101000), negx); Bind(SixTen(0b011111, 0b0001110111), lbzux); Bind(SixTen(0b011111, 0b0001111100), norx); Bind(SixTen(0b011111, 0b0010001000), subfex); Bind(SixTen(0b011111, 0b1010001000), subfex); Bind(SixTen(0b011111, 0b0010001010), addex); Bind(SixTen(0b011111, 0b1010001010), addex); Bind(SixTen(0b011111, 0b0010010000), mtcrf); Bind(SixTen(0b011111, 0b0010010010), mtmsr); Bind(SixTen(0b011111, 0b0010010111), stwx); Bind(SixTen(0b011111, 0b0010110111), stwux); Bind(SixTen(0b011111, 0b0011001000), subfzex); Bind(SixTen(0b011111, 0b1011001000), subfzex); Bind(SixTen(0b011111, 0b0011001010), addzex); Bind(SixTen(0b011111, 0b1011001010), addzex); Bind(SixTen(0b011111, 0b0011010111), stbx); Bind(SixTen(0b011111, 0b0011101000), subfmex); Bind(SixTen(0b011111, 0b1011101000), subfmex); Bind(SixTen(0b011111, 0b0011101010), addmex); Bind(SixTen(0b011111, 0b1011101010), addmex); Bind(SixTen(0b011111, 0b0011101011), mullwx); Bind(SixTen(0b011111, 0b1011101011), mullwx); Bind(SixTen(0b011111, 0b0011110110), dcbtst); Bind(SixTen(0b011111, 0b0011110111), stbux); Bind(SixTen(0b011111, 0b0100001010), addx); Bind(SixTen(0b011111, 0b1100001010), addx); Bind(SixTen(0b011111, 0b0100010110), dcbt); Bind(SixTen(0b011111, 0b0100010111), lhzx); Bind(SixTen(0b011111, 0b0100011100), eqvx); Bind(SixTen(0b011111, 0b0100110110), eciwx); Bind(SixTen(0b011111, 0b0100110111), lhzux); Bind(SixTen(0b011111, 0b0100111100), xorx); Bind(SixTen(0b011111, 0b0101010111), lhax); Bind(SixTen(0b011111, 0b0101110011), mftb); Bind(SixTen(0b011111, 0b0101110111), lhaux); Bind(SixTen(0b011111, 0b0110010111), sthx); Bind(SixTen(0b011111, 0b0110011100), orcx); Bind(SixTen(0b011111, 0b0110110110), ecowx); Bind(SixTen(0b011111, 0b0110110111), sthux); Bind(SixTen(0b011111, 0b0110111100), orx); Bind(SixTen(0b011111, 0b0111001011), divwux); Bind(SixTen(0b011111, 0b1111001011), divwux); Bind(SixTen(0b011111, 0b0111010110), dcbi); Bind(SixTen(0b011111, 0b0111011100), nandx); Bind(SixTen(0b011111, 0b0111101011), divwx); Bind(SixTen(0b011111, 0b1111101011), divwx); Bind(SixTen(0b011111, 0b1000000000), mcrxr); Bind(SixTen(0b011111, 0b1000010101), lswx); Bind(SixTen(0b011111, 0b1000010110), lwbrx); Bind(SixTen(0b011111, 0b1000010111), lfsx); Bind(SixTen(0b011111, 0b1000011000), srwx); Bind(SixTen(0b011111, 0b1000110111), lfsux); Bind(SixTen(0b011111, 0b1001010101), lswi); Bind(SixTen(0b011111, 0b1001010110), sync); Bind(SixTen(0b011111, 0b1001010111), lfdx); Bind(SixTen(0b011111, 0b1001110111), lfdux); Bind(SixTen(0b011111, 0b1010010101), stswx); Bind(SixTen(0b011111, 0b1010010110), stwbrx); Bind(SixTen(0b011111, 0b1010010111), stfsx); Bind(SixTen(0b011111, 0b1010110111), stfsux); Bind(SixTen(0b011111, 0b1011010101), stswi); Bind(SixTen(0b011111, 0b1011010111), stfdx); Bind(SixTen(0b011111, 0b1011110111), stfdux); Bind(SixTen(0b011111, 0b1100010110), lhbrx); Bind(SixTen(0b011111, 0b1100011000), srawx); Bind(SixTen(0b011111, 0b1100111000), srawix); Bind(SixTen(0b011111, 0b1101010110), eieio); Bind(SixTen(0b011111, 0b1110010110), sthbrx); Bind(SixTen(0b011111, 0b1110011010), extshx); Bind(SixTen(0b011111, 0b1110111010), extsbx); Bind(SixTen(0b011111, 0b1111010110), icbi); Bind(SixTen(0b011111, 0b1111010111), stfiwx); Bind(SixTen(0b011111, 0b1111110110), dcbz); Bind(SixTen(0b111111, 0b0000000000), fcmpu); Bind(SixTen(0b111111, 0b0000001100), frspx); Bind(SixTen(0b111111, 0b0000001110), fctiwx); Bind(SixTen(0b111111, 0b0000001111), fctiwzx); Bind(SixTen(0b111111, 0b0000100000), fcmpo); Bind(SixTen(0b111111, 0b0000100110), mtfsb1x); Bind(SixTen(0b111111, 0b0000101000), fnegx); Bind(SixTen(0b111111, 0b0001000000), mcrfs); Bind(SixTen(0b111111, 0b0001000110), mtfsb0x); Bind(SixTen(0b111111, 0b0001001000), fmrx); Bind(SixTen(0b111111, 0b0010000110), mtfsfix); Bind(SixTen(0b111111, 0b0010001000), fnabsx); Bind(SixTen(0b111111, 0b0100001000), fabsx); Bind(SixTen(0b111111, 0b1001000111), mffsx); Bind(SixTen(0b111111, 0b1011000111), mtfsfx); Bind(SixTen(0b111111, 0b1100101110), fctidx); Bind(SixTen(0b111111, 0b1100101111), fctidzx); Bind(SixTen(0b111111, 0b1101001110), fcfidx); Bind(SixTen(0b011111, 0b0101010011), mfspr); // Flagged as "supervisor and user"? Bind(SixTen(0b011111, 0b0111010011), mtspr); // Flagged as "supervisor and user"? BindSupervisorConditional(is32bit, SixTen(0b011111, 0b0011010010), mtsr); BindSupervisorConditional(is32bit, SixTen(0b011111, 0b0011110010), mtsrin); BindSupervisorConditional(is32bit, SixTen(0b011111, 0b1001010011), mfsr); BindSupervisorConditional(is32bit, SixTen(0b011111, 0b1010010011), mfsrin); BindSupervisorConditional(is64bit, SixTen(0b011111, 0b0111110010), slbia); // optional // The following are all optional; should I record that? BindSupervisor(SixTen(0b011111, 0b0100110010), tlbie); BindSupervisor(SixTen(0b011111, 0b0101110010), tlbia); BindSupervisor(SixTen(0b011111, 0b1000110110), tlbsync); } // Third pass: like six-ten except that the top five of the final ten // are reserved (i.e. ignored here). switch(opcode & SixTen(0b111111, 0b11111)) { default: break; Bind(SixTen(0b111011, 0b10010), fdivsx); Bind(SixTen(0b111011, 0b10100), fsubsx); Bind(SixTen(0b111011, 0b10101), faddsx); Bind(SixTen(0b111011, 0b11001), fmulsx); Bind(SixTen(0b111011, 0b11100), fmsubsx); Bind(SixTen(0b111011, 0b11101), fmaddsx); Bind(SixTen(0b111011, 0b11110), fnmsubsx); Bind(SixTen(0b111011, 0b11111), fnmaddsx); Bind(SixTen(0b111111, 0b10010), fdivx); Bind(SixTen(0b111111, 0b10100), fsubx); Bind(SixTen(0b111111, 0b10101), faddx); Bind(SixTen(0b111111, 0b11001), fmulx); Bind(SixTen(0b111111, 0b11100), fmsubx); Bind(SixTen(0b111111, 0b11101), fmaddx); Bind(SixTen(0b111111, 0b11110), fnmsubx); Bind(SixTen(0b111111, 0b11111), fnmaddx); BindConditional(is64bit, SixTen(0b111011, 0b10110), fsqrtsx); BindConditional(is64bit, SixTen(0b111011, 0b11000), fresx); // Optional... Bind(SixTen(0b111111, 0b10110), fsqrtx); Bind(SixTen(0b111111, 0b10111), fselx); Bind(SixTen(0b111111, 0b11010), frsqrtex); } // stwcx. and stdcx. switch(opcode & 0b111111'00'00000000'000'111111111'1){ case 0b011111'00'00000000'00000'0010010110'1: return Instruction(Operation::stwcx_, opcode); case 0b011111'00'00000000'00000'0011010110'1: if(is64bit()) return Instruction(Operation::stdcx_, opcode); return Instruction(opcode); } // std and stdu switch(opcode & 0b111111'00'00000000'00000000'000000'11){ case 0b111110'00'00000000'00000000'000000'00: return Instruction(Operation::std, opcode); case 0b111110'00'00000000'00000000'000000'01: if(is64bit()) return Instruction(Operation::stdu, opcode); return Instruction(opcode); } // sc if((opcode & 0b111111'00'00000000'00000000'000000'1'0) == 0b010001'00'00000000'00000000'000000'1'0) { return Instruction(Operation::sc, opcode); } #undef Six #undef SixTen #undef Bind #undef BindConditional return Instruction(opcode); }