diff --git a/InstructionSets/PowerPC/Instruction.hpp b/InstructionSets/PowerPC/Instruction.hpp index 59d02114e..b67e25c12 100644 --- a/InstructionSets/PowerPC/Instruction.hpp +++ b/InstructionSets/PowerPC/Instruction.hpp @@ -21,6 +21,22 @@ enum class CacheLine: uint32_t { Maximum = 0b01111, }; +enum class Condition: uint32_t { + // CR0 + Negative = 0, // LT + Positive = 1, // GT + Zero = 2, // EQ + SummaryOverflow = 3, // SO + + // CR1 + FPException = 4, // FX + FPEnabledException = 5, // FEX + FPInvalidException = 6, // VX + FPOverflowException = 7, // OX + + // CRs2–7 fill out the condition register. +}; + enum class BranchOptions: uint32_t { // Naming convention: // @@ -33,15 +49,15 @@ enum class BranchOptions: uint32_t { // // Note that the encodings themselves may suggest alternative means // of interpretation than mapping via this enum. - Dec_NotZeroAndClear1 = 0b00000, Dec_NotZeroAndClear2 = 0b00001, - Dec_ZeroAndClear1 = 0b00010, Dec_ZeroAndClear2 = 0b00011, - Clear1 = 0b00100, Clear2 = 0b00101, - Dec_NotZeroAndSet1 = 0b01000, Dec_NotZeroAndSet2 = 0b01001, - Dec_ZeroAndSet1 = 0b01010, Dec_ZeroAndSet2 = 0b01011, - Set1 = 0b01100, Set2 = 0b01101, - Dec_NotZero1 = 0b10000, Dec_NotZero2 = 0b10001, - Dec_Zero1 = 0b10010, Dec_Zero2 = 0b10011, - Always = 0b10100, + Dec_NotZeroAndClear = 0b0000, + Dec_ZeroAndClear = 0b0001, + Clear = 0b0010, + Dec_NotZeroAndSet = 0b0100, + Dec_ZeroAndSet = 0b0101, + Set = 0b0110, + Dec_NotZero = 0b1000, + Dec_Zero = 0b1001, + Always = 0b1010, }; enum class Operation: uint8_t { @@ -106,18 +122,8 @@ enum class Operation: uint8_t { /// /// lk() determines whether to update the link register. /// bd() supplies a relative displacment. - /// bi() specifies which CR bit to use as a condition. - /// bo() provides other branch options: - /// 0000y => decrement count register, branch if new value != 0 && !condition. - /// 0001y => decrement count register, branch if new value == 0 && !condition. - /// 001zy => branch if !condition - /// 0100y => decrement count register, branch if new value != 0 && condition. - /// 0101y => decrement count register, branch if new value == 0 && condition. - /// 011zy => branch if condition. - /// 1z00y => decrement count register, branch if new value != 0. - /// 1z01y => decrement count register, branch if new value == 0. - /// 1z1zz => branch always. - /// (z should be 0 to create a valid field; y can be any value) + /// bi() specifies which CR bit to use as a condition; cf. the Condition enum. + /// bo() provides other branch options and a branch prediction hint as per (BranchOptions enum << 1) | hint. /// /// Synonyms incude: /// * b (relative, no link) [though assemblers might encode as a bx]. @@ -225,8 +231,16 @@ struct Instruction { /// Floating point register destination. uint32_t frD() const { return (opcode >> 21) & 0x1f; } - /// Branch conditional options. Cf. the BranchOptions enum. + /// Branch conditional options as per PowerPC spec, i.e. options + branch-prediction flag. uint32_t bo() const { return (opcode >> 21) & 0x1f; } + /// Just the branch options, with the branch prediction flag severed. + BranchOptions branch_options() const { + return BranchOptions((opcode >> 22) & 0xf); + } + /// Just the branch-prediction hint; @c 0 => expect untaken; @c non-0 => expect take. + uint32_t branch_prediction_hint() const { + return opcode & 0x200000; + } /// Source condition register bit for branch conditionals. uint32_t bi() const { return (opcode >> 16) & 0x1f; } /// Branch displacement; provided as already sign extended. diff --git a/OSBindings/Mac/Clock SignalTests/DingusdevPowerPCTests.mm b/OSBindings/Mac/Clock SignalTests/DingusdevPowerPCTests.mm index c40f99609..d5ebba990 100644 --- a/OSBindings/Mac/Clock SignalTests/DingusdevPowerPCTests.mm +++ b/OSBindings/Mac/Clock SignalTests/DingusdevPowerPCTests.mm @@ -12,10 +12,7 @@ #include "../../../InstructionSets/PowerPC/Decoder.hpp" -namespace { - using Operation = InstructionSet::PowerPC::Operation; - using Instruction = InstructionSet::PowerPC::Instruction; -} +using namespace InstructionSet::PowerPC; @interface DingusdevPowerPCTests : XCTestCase @end @@ -45,7 +42,7 @@ namespace { NSArray *const columns = [line componentsSeparatedByString:@","]; - // Column 1: address. + // Columns are 1: address; 2: opcode; 3–: specific to the instruction. const uint32_t address = uint32_t(std::strtol([columns[0] UTF8String], 0, 16)); const uint32_t opcode = uint32_t(std::strtol([columns[1] UTF8String], 0, 16)); NSString *const operation = columns[2]; @@ -56,9 +53,58 @@ namespace { NSAssert(FALSE, @"Didn't handle %@", line); break; + case Operation::bcctrx: { + NSString *baseOperation = nil; + switch(instruction.branch_options()) { + case BranchOptions::Always: baseOperation = @"bctr"; break; + case BranchOptions::Clear: + switch(Condition(instruction.bi() & 3)) { + default: break; + case Condition::Negative: baseOperation = @"bgectr"; break; + case Condition::Positive: baseOperation = @"blectr"; break; + case Condition::Zero: baseOperation = @"bnectr"; break; + case Condition::SummaryOverflow: + baseOperation = @"bsoctr"; + break; + } + break; + case BranchOptions::Set: + switch(Condition(instruction.bi() & 3)) { + default: break; + case Condition::Negative: baseOperation = @"bltctr"; break; + case Condition::Positive: baseOperation = @"bgtctr"; break; + case Condition::Zero: baseOperation = @"beqctr"; break; + case Condition::SummaryOverflow: + baseOperation = @"bnsctr"; + break; + } + break; + default: break; + } + + if(!baseOperation) { + NSAssert(FALSE, @"Didn't handle bi %d for bo %d, %@", instruction.bi() & 3, instruction.bo(), line); + } else { + if(instruction.lk()) { + baseOperation = [baseOperation stringByAppendingString:@"l"]; + } + if(instruction.branch_prediction_hint()) { + baseOperation = [baseOperation stringByAppendingString:@"+"]; + } + XCTAssertEqualObjects(operation, baseOperation); + } + + if(instruction.bi() & ~3) { + NSString *const expectedCR = [NSString stringWithFormat:@"cr%d", instruction.bi() >> 2]; + XCTAssertEqualObjects(columns[3], expectedCR); + } + } break; + case Operation::bcx: { - switch(instruction.bo()) { - case 0x14: case 0x15: XCTAssertEqualObjects(operation, @"b"); break; + switch(instruction.branch_options()) { + case BranchOptions::Always: + XCTAssertEqualObjects(operation, @"b"); + break; default: NSLog(@"No opcode tested for bcx with bo %02x", instruction.bo()); break;