diff --git a/InstructionSets/PowerPC/Instruction.hpp b/InstructionSets/PowerPC/Instruction.hpp index 155fe8dd3..349715148 100644 --- a/InstructionSets/PowerPC/Instruction.hpp +++ b/InstructionSets/PowerPC/Instruction.hpp @@ -63,8 +63,10 @@ enum class BranchOption: uint32_t { enum class Operation: uint8_t { Undefined, + // // These 601-exclusive instructions; a lot of them are carry-overs // from POWER. These are not part of the PowerPC architecture. + // /// |rA| is placed into rD. If rA = 0x8000'0000 then 0x8000'0000 is placed into rD /// and XER[OV] is set if oe() indicates that overflow is enabled. @@ -100,8 +102,35 @@ enum class Operation: uint8_t { nabsx, rlmix, rribx, slex, sleqx, sliqx, slliqx, sllqx, slqx, sraiqx, sraqx, srex, sreax, sreqx, sriqx, srliqx, srlqx, srqx, + // // 32- and 64-bit PowerPC instructions. - addx, addcx, addex, addi, addic, addic_, addis, addmex, addzex, andx, + // + + addx, addcx, addex, + + /// Add immediate. + /// + /// rD() = (rA() | 0) + simm() + addi, + + /// Add immediate carrying. + /// + /// rD() = (rA() | 0) + simm() + /// XER[CA] = carry. + addic, + + /// Add immediate carrying. + /// + /// rD() = (rA() | 0) + simm() + /// XER[CA] = carry, and the condition register is updated. + addic_, + + /// Add immediate shifter. + /// + /// rD() = (rA() | 0) + (simm() << 16) + addis, + + addmex, addzex, andx, andcx, andi_, andis_, /// Branch unconditional. @@ -169,7 +198,45 @@ enum class Operation: uint8_t { lbzx, lfd, lfdu, lfdux, lfdx, lfs, lfsu, - lfsux, lfsx, lha, lhau, lhaux, lhax, lhbrx, lhz, lhzu, lhzux, lhzx, lmw, + lfsux, lfsx, lha, lhau, + + /// Load half-word algebraic with update indexed. + /// + /// rD()[16, 31] = [ rA()|0 + rB() ]; and rA() is set to the calculated address + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + /// The result in rD is sign extended. + /// + /// PowerPC defines rA=0 and rA=rD to be invalid forms; the MPC601 + /// will suppress the update if rA=0 or rA=rD. + lhaux, + + /// Load half-word algebraic indexed. + /// + /// rD[16, 31] = [ (rA()|0) + rB() ] + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + /// The result in rD is sign extended. + lhax, + + lhbrx, lhz, lhzu, + + /// Load half-word and zero with update indexed. + /// + /// rD()[16, 31] = [ rA()|0 + rB() ]; and rA() is set to the calculated address + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + /// The rest of rD is set to 0. + /// + /// PowerPC defines rA=0 and rA=rD to be invalid forms; the MPC601 + /// will suppress the update if rA=0 or rA=rD. + lhzux, + + /// Load half-word and zero indexed. + /// + /// rD[16, 31] = [ (rA()|0) + rB() ] + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + /// The rest of rD is set to 0. + lhzx, + + lmw, lswi, lswx, lwarx, lwbrx, lwz, lwzu, /// Load word and zero with update indexed. @@ -189,12 +256,74 @@ enum class Operation: uint8_t { mcrf, mcrfs, mcrxr, mfcr, mffsx, mfmsr, mfspr, mfsr, mfsrin, mtcrf, mtfsb0x, mtfsb1x, mtfsfx, - mtfsfix, mtmsr, mtspr, mtsr, mtsrin, mulhwx, mulhwux, mulli, mullwx, + mtfsfix, mtmsr, mtspr, mtsr, mtsrin, mulhwx, mulhwux, + + /// Multiply low immediate. + /// + /// rD() = [low 32 bits of] rA() * simm() + /// XER[OV] is set if, were the operands treated as signed, overflow occurred. + mulli, + + mullwx, nandx, negx, norx, orx, orcx, ori, oris, rfi, rlwimix, rlwinmx, rlwnmx, - sc, slwx, srawx, srawix, srwx, stb, stbu, stbux, stbx, stfd, stfdu, - stfdux, stfdx, stfs, stfsu, stfsux, stfsx, sth, sthbrx, sthu, sthux, sthx, - stmw, stswi, stswx, stw, stwbrx, stwcx_, stwu, stwux, stwx, subfx, subfcx, - subfex, subfic, subfmex, subfzex, sync, tw, twi, xorx, xori, xoris, mftb, + sc, slwx, srawx, srawix, srwx, stb, stbu, + + /// Store byte with update indexed. + /// + /// [ (ra()|0) + rB() ] = rS()[24, 31]; and rA() is updated with the calculated address. + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + /// + /// PowerPC defines rA=0 to an invalid form; the MPC601 will store to r0. + stbux, + + /// Store byte indexed. + /// + /// [ (ra()|0) + rB() ] = rS()[24, 31] + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + stbx, + + stfd, stfdu, + stfdux, stfdx, stfs, stfsu, stfsux, stfsx, sth, sthbrx, sthu, + + /// Store half-word with update indexed. + /// + /// [ (ra()|0) + rB() ] = rS()[16, 31]; and rA() is updated with the calculated address. + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + /// + /// PowerPC defines rA=0 to an invalid form; the MPC601 will store to r0. + sthux, + + /// Store half-word indexed. + /// + /// [ (ra()|0) + rB() ] = rS()[16, 31] + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + sthx, + + stmw, stswi, stswx, stw, stwbrx, stwcx_, stwu, + + /// Store word with update indexed. + /// + /// [ (ra()|0) + rB() ] = rS(); and rA() is updated with the calculated address. + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + /// + /// PowerPC defines rA=0 to an invalid form; the MPC601 will store to r0. + stwux, + + /// Store word indexed. + /// + /// [ (ra()|0) + rB() ] = rS() + /// i.e. if rA() is 0 then the value 0 is used, not the contents of r0. + stwx, + + subfx, subfcx, + subfex, + + /// Subtract from immediate carrying + /// + /// rD() = ~rA() + simm() + 1 + subfic, + + subfmex, subfzex, sync, tw, twi, xorx, xori, xoris, mftb, // 32-bit, supervisor level. dcbi, @@ -205,7 +334,9 @@ enum class Operation: uint8_t { // Optional. fresx, frsqrtex, fselx, fsqrtx, slbia, slbie, stfiwx, + // // 64-bit only PowerPC instructions. + // cntlzdx, divdx, divdux, extswx, fcfidx, fctidx, fctidzx, tdi, mulhdux, ldx, sldx, ldux, td, mulhdx, ldarx, stdx, stdux, mulld, lwax, lwaux, sradix, srdx, sradx, extsw, fsqrtsx, std, stdu, stdcx_, diff --git a/OSBindings/Mac/Clock SignalTests/DingusdevPowerPCTests.mm b/OSBindings/Mac/Clock SignalTests/DingusdevPowerPCTests.mm index 53138ec91..d858c8392 100644 --- a/OSBindings/Mac/Clock SignalTests/DingusdevPowerPCTests.mm +++ b/OSBindings/Mac/Clock SignalTests/DingusdevPowerPCTests.mm @@ -17,6 +17,16 @@ using namespace InstructionSet::PowerPC; @interface DingusdevPowerPCTests : XCTestCase @end +namespace { + +void AssertEqualOperationName(NSString *lhs, NSString *rhs) { + NSString *const lhsMapped = [lhs stringByReplacingOccurrencesOfString:@"_" withString:@"."]; + NSString *const rhsMapped = [rhs stringByReplacingOccurrencesOfString:@"_" withString:@"."]; + XCTAssertEqualObjects(lhsMapped, rhsMapped); +} + +} + @implementation DingusdevPowerPCTests - (void)testABDInstruction:(Instruction)instruction columns:(NSArray *)columns testZero:(BOOL)testZero { @@ -57,21 +67,52 @@ using namespace InstructionSet::PowerPC; NSString *const operation = columns[2]; const auto instruction = decoder.decode(opcode); + NSLog(@"%@", line); switch(instruction.operation) { default: NSAssert(FALSE, @"Didn't handle %@", line); break; +#define ArithImm(x) \ + case Operation::x: { \ + NSString *const rD = [NSString stringWithFormat:@"r%d", instruction.rD()]; \ + NSString *const rA = [NSString stringWithFormat:@"r%d", instruction.rA()]; \ + const auto simm = strtol([columns[5] UTF8String], NULL, 16); \ + AssertEqualOperationName(operation, @#x); \ + XCTAssertEqualObjects(columns[3], rD); \ + XCTAssertEqualObjects(columns[4], rA); \ + XCTAssertEqual(simm, instruction.simm()); \ + } break; + + ArithImm(mulli); + ArithImm(subfic); + ArithImm(addi); + ArithImm(addic); + ArithImm(addic_); + ArithImm(addis); + +#undef ArithImm + #define ABCz(x) \ case Operation::x: \ - XCTAssertEqualObjects(operation, @#x); \ + AssertEqualOperationName(operation, @#x); \ [self testABDInstruction:instruction columns:columns testZero:YES]; \ break; - ABCz(lwzux); ABCz(lwzx); + ABCz(lwzux); ABCz(lbzx); ABCz(lbzux); + ABCz(stwx); + ABCz(stwux); + ABCz(stbx); + ABCz(stbux); + ABCz(lhzx); + ABCz(lhzux); + ABCz(lhax); + ABCz(lhaux); + ABCz(sthx); + ABCz(sthux); #undef ABCz @@ -164,7 +205,7 @@ using namespace InstructionSet::PowerPC; if(instruction.branch_prediction_hint()) { baseOperation = [baseOperation stringByAppendingString:@"+"]; } - XCTAssertEqualObjects(operation, baseOperation); + AssertEqualOperationName(operation, baseOperation); } if(instruction.bi() & ~3) { @@ -190,10 +231,10 @@ using namespace InstructionSet::PowerPC; case Operation::bx: { switch((instruction.aa() ? 2 : 0) | (instruction.lk() ? 1 : 0)) { - case 0: XCTAssertEqualObjects(operation, @"b"); break; - case 1: XCTAssertEqualObjects(operation, @"bl"); break; - case 2: XCTAssertEqualObjects(operation, @"ba"); break; - case 3: XCTAssertEqualObjects(operation, @"bla"); break; + case 0: AssertEqualOperationName(operation, @"b"); break; + case 1: AssertEqualOperationName(operation, @"bl"); break; + case 2: AssertEqualOperationName(operation, @"ba"); break; + case 3: AssertEqualOperationName(operation, @"bla"); break; } const uint32_t destination = uint32_t(std::strtol([columns[3] UTF8String], 0, 16));