// // DingusdevPowerPCTests.mm // Clock Signal // // Created by Thomas Harte on 18/03/2022. // Copyright 2022 Thomas Harte. All rights reserved. // #import #include #include "../../../InstructionSets/PowerPC/Decoder.hpp" using namespace InstructionSet::PowerPC; @interface NSString (HexConversion) - (long int)hexInt; @end @implementation NSString (HexConversion) - (long int)hexInt { return strtol([self UTF8String], NULL, 16); } @end @interface DingusdevPowerPCTests : XCTestCase @end namespace { enum class NamingConvention { None = 0, ApplyO = 1, ApplyE = 2, ApplyOE = 3 }; /// Converts any underscores in @c rhs to dots. /// /// If convention != NamingConvention::None, trims the trailing 'x' from the @c rhs and /// appends one or more of @c o and @c . depending on the @c oe() and @c rc() flags of @c instruction. /// /// Then compares with the @c lhs. void AssertEqualOperationName(NSString *lhs, NSString *rhs, Instruction instruction = Instruction(), NamingConvention convention = NamingConvention::None) { rhs = [rhs stringByReplacingOccurrencesOfString:@"_" withString:@"."]; if(convention != NamingConvention::None) { XCTAssert([rhs characterAtIndex:rhs.length - 1] == 'x'); rhs = [rhs substringToIndex:rhs.length - 1]; } if(int(convention) & int(NamingConvention::ApplyO) && instruction.oe()) rhs = [rhs stringByAppendingString:@"o"]; if(int(convention) & int(NamingConvention::ApplyE) && instruction.rc()) rhs = [rhs stringByAppendingString:@"."]; XCTAssertEqualObjects(lhs, rhs); } void AssertEqualOperationNameE(NSString *lhs, NSString *rhs, Instruction instruction) { AssertEqualOperationName(lhs, rhs, instruction, NamingConvention::ApplyE); } void AssertEqualOperationNameOE(NSString *lhs, NSString *rhs, Instruction instruction) { AssertEqualOperationName(lhs, rhs, instruction, NamingConvention::ApplyOE); } /// Forms the string @c r[reg] and compares it to @c name void AssertEqualR(NSString *name, uint32_t reg, bool permitR0 = true) { NSString *const regName = (reg || permitR0) ? [NSString stringWithFormat:@"r%d", reg] : @"0"; XCTAssertEqualObjects(name, regName); } /// Forms the string @c f[reg] and compares it to @c name void AssertEqualFR(NSString *name, uint32_t reg, bool permitR0 = true) { NSString *const regName = (reg || permitR0) ? [NSString stringWithFormat:@"f%d", reg] : @"0"; XCTAssertEqualObjects(name, regName); } /// @returns the text name of the condition code @c code NSString *condition(uint32_t code) { NSString *suffix; switch(Condition(code & 3)) { default: break; case Condition::Negative: suffix = @"lt"; break; case Condition::Positive: suffix = @"gt"; break; case Condition::Zero: suffix = @"eq"; break; case Condition::SummaryOverflow: suffix = @"so"; break; } if(code & ~3) { return [NSString stringWithFormat:@"4*cr%d+%@", code >> 2, suffix]; } else { return suffix; } } NSString *conditionreg(uint32_t code) { return [NSString stringWithFormat:@"cr%d", code]; } NSString *offset(Instruction instruction) { NSString *const hexPart = [NSString stringWithFormat:@"%s%X", (instruction.d() < 0) ? "-0x" : "0x", abs(instruction.d())]; if(instruction.rA()) { return [hexPart stringByAppendingFormat:@"(r%d)", instruction.rA()]; } else { return hexPart; } } } @implementation DingusdevPowerPCTests - (void)testDecoding { NSData *const testData = [NSData dataWithContentsOfURL: [[NSBundle bundleForClass:[self class]] URLForResource:@"ppcdisasmtest" withExtension:@"csv" subdirectory:@"dingusdev PowerPC tests"]]; NSString *const wholeFile = [[NSString alloc] initWithData:testData encoding:NSUTF8StringEncoding]; NSArray *const lines = [wholeFile componentsSeparatedByString:@"\n"]; InstructionSet::PowerPC::Decoder decoder; for(NSString *const line in lines) { // Ignore empty lines and comments. if([line length] == 0) { continue; } if([line characterAtIndex:0] == '#') { continue; } NSArray *const columns = [line componentsSeparatedByString:@","]; // Columns are 1: address; 2: opcode; 3–: specific to the instruction. const auto address = uint32_t([columns[0] hexInt]); const auto opcode = uint32_t([columns[1] hexInt]); NSString *const operation = columns[2]; const auto instruction = decoder.decode(opcode); // Deal with some of the simplified mnemonics as special cases; // underlying observation: distinguishing special cases isn't that // important to a mere decoder. if([operation isEqualToString:@"nop"]) { // These tests use ORI for NOP. XCTAssertEqual(instruction.operation, Operation::ori); XCTAssertEqual(instruction.rD(), instruction.rA()); XCTAssertEqual(instruction.simm(), 0); continue; } if([operation isEqualToString:@"mr"] || [operation isEqualToString:@"mr."]) { // OR is used to achieve MR (i.e. move register). XCTAssertEqual(instruction.operation, Operation::orx); XCTAssertEqual((instruction.rc() != 0), ([operation characterAtIndex:operation.length - 1] == '.')); AssertEqualR(columns[3], instruction.rA()); AssertEqualR(columns[4], instruction.rS()); AssertEqualR(columns[4], instruction.rB()); continue; } if([operation isEqualToString:@"li"]) { // ADDI is used to achieve LI (i.e. load immediate). XCTAssertEqual(instruction.operation, Operation::addi); AssertEqualR(columns[3], instruction.rD()); XCTAssertEqual(0, instruction.rA()); XCTAssertEqual([columns[4] hexInt], instruction.simm()); continue; } switch(instruction.operation) { default: NSAssert(FALSE, @"Didn't handle %@", line); break; case Operation::Undefined: XCTAssertEqualObjects(operation, @"dc.l"); break; case Operation::rlwimix: { // This maps the opposite way from most of the other tests // owing to the simplified names being a shade harder to // detect motivationally. XCTAssertEqual((instruction.rc() != 0), ([operation characterAtIndex:operation.length - 1] == '.')); AssertEqualR(columns[3], instruction.rA()); AssertEqualR(columns[4], instruction.rS()); const auto n = [columns[5] intValue]; const auto b = [columns[6] intValue]; if([operation isEqualToString:@"inslwi"] || [operation isEqualToString:@"inslwi."]) { XCTAssertEqual(instruction.sh(), 32 - b); XCTAssertEqual(instruction.mb(), b); XCTAssertEqual(instruction.me(), b + n - 1); break; } if([operation isEqualToString:@"insrwi"] || [operation isEqualToString:@"insrwi."]) { XCTAssertEqual(instruction.sh(), 32 - (b + n)); XCTAssertEqual(instruction.mb(), b); XCTAssertEqual(instruction.me(), b + n - 1); break; } NSAssert(FALSE, @"Didn't handle rlwimix %@", line); } break; case Operation::rlwnmx: { XCTAssertEqual((instruction.rc() != 0), ([operation characterAtIndex:operation.length - 1] == '.')); AssertEqualR(columns[3], instruction.rA()); AssertEqualR(columns[4], instruction.rS()); AssertEqualR(columns[5], instruction.rB()); if([operation isEqualToString:@"rotlw"] || [operation isEqualToString:@"rotlw."]) { XCTAssertEqual(instruction.mb(), 0); XCTAssertEqual(instruction.me(), 31); break; } NSAssert(FALSE, @"Didn't handle rlwnmx %@", line); } break; case Operation::rlwinmx: { // This maps the opposite way from most of the other tests // owing to the simplified names being a shade harder to // detect motivationally. XCTAssertEqual((instruction.rc() != 0), ([operation characterAtIndex:operation.length - 1] == '.')); AssertEqualR(columns[3], instruction.rA()); AssertEqualR(columns[4], instruction.rS()); const auto n = [columns[5] intValue]; const auto b = columns.count > 6 ? [columns[6] intValue] : 0; if([operation isEqualToString:@"extlwi"] || [operation isEqualToString:@"extlwi."]) { XCTAssertEqual(instruction.sh(), b); XCTAssertEqual(instruction.mb(), 0); XCTAssertEqual(instruction.me(), n - 1); break; } if([operation isEqualToString:@"extrwi"] || [operation isEqualToString:@"extrwi."]) { XCTAssertEqual(instruction.sh(), b + n); XCTAssertEqual(instruction.mb(), 32 - n); XCTAssertEqual(instruction.me(), 31); break; } if([operation isEqualToString:@"rotlwi"] || [operation isEqualToString:@"rotlwi."]) { XCTAssertEqual(instruction.sh(), n); XCTAssertEqual(instruction.mb(), 0); XCTAssertEqual(instruction.me(), 31); break; } if([operation isEqualToString:@"rotrwi"] || [operation isEqualToString:@"rotrwi."]) { XCTAssertEqual(instruction.sh(), 32 - n); XCTAssertEqual(instruction.mb(), 0); XCTAssertEqual(instruction.me(), 31); break; } if([operation isEqualToString:@"slwi"] || [operation isEqualToString:@"slwi."]) { XCTAssertEqual(instruction.sh(), n); XCTAssertEqual(instruction.mb(), 0); XCTAssertEqual(instruction.me(), 31 - n); break; } if([operation isEqualToString:@"srwi"] || [operation isEqualToString:@"srwi."]) { XCTAssertEqual(instruction.sh(), 32 - n); XCTAssertEqual(instruction.mb(), n); XCTAssertEqual(instruction.me(), 31); break; } if([operation isEqualToString:@"clrlwi"] || [operation isEqualToString:@"clrlwi."]) { XCTAssertEqual(instruction.sh(), 0); XCTAssertEqual(instruction.mb(), n); XCTAssertEqual(instruction.me(), 31); break; } if([operation isEqualToString:@"clrrwi"] || [operation isEqualToString:@"clrrwi."]) { XCTAssertEqual(instruction.sh(), 0); XCTAssertEqual(instruction.mb(), 0); XCTAssertEqual(instruction.me(), 31 - n); break; } if([operation isEqualToString:@"clrlslwi"] || [operation isEqualToString:@"clrlslwi."]) { // FreeScale switched the order of b and n in the short-form instruction here; // they are therefore transposed in the tests below. XCTAssertEqual(instruction.sh(), b); XCTAssertEqual(instruction.mb(), n - b); XCTAssertEqual(instruction.me(), 31 - b); break; } NSAssert(FALSE, @"Didn't handle rlwinmx %@", line); } break; case Operation::tw: AssertEqualOperationName(operation, @"tw"); XCTAssertEqual([columns[3] intValue], instruction.to()); AssertEqualR(columns[4], instruction.rA()); AssertEqualR(columns[5], instruction.rB()); break; case Operation::twi: AssertEqualOperationName(operation, @"twi"); XCTAssertEqual([columns[3] intValue], instruction.to()); AssertEqualR(columns[4], instruction.rA()); XCTAssertEqual([columns[5] hexInt], instruction.simm()); break; case Operation::mtfsfx: AssertEqualOperationNameE(operation, @"mtfsfx", instruction); XCTAssertEqual([columns[3] intValue], instruction.fm()); AssertEqualFR(columns[4], instruction.frB()); break; case Operation::mtfsfix: AssertEqualOperationNameE(operation, @"mtfsfix", instruction); XCTAssertEqualObjects(columns[3], conditionreg(instruction.crfD())); XCTAssertEqual([columns[4] intValue], instruction.imm()); break; case Operation::mcrxr: AssertEqualOperationName(operation, @"mcrxr"); XCTAssertEqualObjects(columns[3], conditionreg(instruction.crfD())); break; case Operation::mffsx: AssertEqualOperationNameE(operation, @"mffsx", instruction); AssertEqualFR(columns[3], instruction.frD()); break; case Operation::mtmsr: AssertEqualOperationName(operation, @"mtmsr"); AssertEqualR(columns[3], instruction.rS()); break; case Operation::mtsrin: AssertEqualOperationName(operation, @"mtsrin"); AssertEqualR(columns[3], instruction.rD()); AssertEqualR(columns[4], instruction.rB()); break; case Operation::clcs: AssertEqualOperationName(operation, @"clcs"); AssertEqualR(columns[3], instruction.rD()); AssertEqualR(columns[4], instruction.rA()); break; case Operation::mtsr: AssertEqualOperationName(operation, @"mtsr"); XCTAssertEqual([columns[3] intValue], instruction.sr()); AssertEqualR(columns[4], instruction.rS()); break; case Operation::tlbie: AssertEqualOperationName(operation, @"tlbie"); AssertEqualR(columns[3], instruction.rB()); break; case Operation::icbi: AssertEqualOperationName(operation, @"icbi"); AssertEqualR(columns[3], instruction.rA(), false); AssertEqualR(columns[4], instruction.rB()); break; case Operation::lswi: AssertEqualOperationName(operation, @"lswi"); AssertEqualR(columns[3], instruction.rD()); AssertEqualR(columns[4], instruction.rA()); XCTAssertEqual([columns[5] hexInt], instruction.nb()); break; case Operation::stswi: AssertEqualOperationName(operation, @"stswi"); AssertEqualR(columns[3], instruction.rS()); AssertEqualR(columns[4], instruction.rA()); XCTAssertEqual([columns[5] hexInt], instruction.nb()); break; case Operation::rlmix: AssertEqualOperationNameE(operation, @"rlmix", instruction); AssertEqualR(columns[3], instruction.rA()); AssertEqualR(columns[4], instruction.rS()); AssertEqualR(columns[5], instruction.rB()); XCTAssertEqual([columns[6] intValue], instruction.mb()); XCTAssertEqual([columns[7] intValue], instruction.me()); break; case Operation::cmp: if([operation isEqualToString:@"cmpw"]) { XCTAssertFalse(instruction.l()); XCTAssertFalse(instruction.crfD()); AssertEqualR(columns[3], instruction.rA()); AssertEqualR(columns[4], instruction.rB()); break; } if([operation isEqualToString:@"cmp"]) { XCTAssertEqualObjects(columns[3], conditionreg(instruction.crfD())); AssertEqualR(columns[4], instruction.rA()); AssertEqualR(columns[5], instruction.rB()); break; } NSAssert(FALSE, @"Didn't handle cmp %@", line); break; case Operation::cmpl: if([operation isEqualToString:@"cmplw"]) { XCTAssertFalse(instruction.l()); if(instruction.crfD()) { XCTAssertEqualObjects(columns[3], conditionreg(instruction.crfD())); AssertEqualR(columns[4], instruction.rA()); AssertEqualR(columns[5], instruction.rB()); } else { AssertEqualR(columns[3], instruction.rA()); AssertEqualR(columns[4], instruction.rB()); } break; } NSAssert(FALSE, @"Didn't handle cmpl %@", line); break; case Operation::cmpli: case Operation::cmpi: if([operation isEqualToString:@"cmpwi"] || [operation isEqualToString:@"cmplwi"]) { XCTAssertFalse(instruction.l()); } else { XCTAssertTrue(instruction.l()); } XCTAssertEqualObjects(columns[3], conditionreg(instruction.crfD())); AssertEqualR(columns[4], instruction.rA()); XCTAssertEqual([columns[5] hexInt], instruction.simm()); break; case Operation::mtfsb0x: case Operation::mtfsb1x: AssertEqualOperationNameE( operation, instruction.operation == Operation::mtfsb0x ? @"mtfsb0x" : @"mtfsb1x", instruction); XCTAssertEqual([columns[3] intValue], instruction.crbD()); break; #define NoArg(x) \ case Operation::x: \ AssertEqualOperationName(operation, @#x); \ break; NoArg(isync); NoArg(sync); NoArg(eieio); NoArg(tlbia); #undef NoArg #define D(x) \ case Operation::x: \ AssertEqualOperationName(operation, @#x); \ AssertEqualR(columns[3], instruction.rD()); \ break; D(mfcr); D(mfmsr); #undef D #define Shift(x) \ case Operation::x: \ AssertEqualOperationNameE(operation, @#x, instruction); \ AssertEqualR(columns[3], instruction.rA()); \ AssertEqualR(columns[4], instruction.rS()); \ AssertEqualR(columns[5], instruction.rB()); \ break; Shift(slwx); Shift(srwx); Shift(srawx); #undef Shift #define ASsh(x) \ case Operation::x: \ AssertEqualOperationNameE(operation, @#x, instruction); \ AssertEqualR(columns[3], instruction.rA()); \ AssertEqualR(columns[4], instruction.rS()); \ XCTAssertEqual([columns[5] hexInt], instruction.sh()); \ break; ASsh(sliqx); ASsh(slliqx); ASsh(sraiqx); ASsh(sriqx); ASsh(srliqx); ASsh(srawix); #undef ASsh #define CRMod(x) \ case Operation::x: \ AssertEqualOperationName(operation, @#x); \ XCTAssertEqualObjects(columns[3], condition(instruction.crbD())); \ XCTAssertEqualObjects(columns[4], condition(instruction.crbA())); \ XCTAssertEqualObjects(columns[5], condition(instruction.crbB())); \ break; CRMod(crand); CRMod(crandc); CRMod(creqv); CRMod(crnand); CRMod(crnor); CRMod(cror); CRMod(crorc); CRMod(crxor); #undef CRMod case Operation::mtcrf: { AssertEqualOperationName(operation, instruction.crm() != 0xff ? @"mtcrf" : @"mtcr"); AssertEqualR([columns lastObject], instruction.rS()); if(columns.count > 4) { XCTAssertEqual([columns[3] hexInt], instruction.crm()); } } break; #define ArithImm(x) \ case Operation::x: { \ AssertEqualOperationName(operation, @#x); \ AssertEqualR(columns[3], instruction.rD()); \ AssertEqualR(columns[4], instruction.rA()); \ XCTAssertEqual([columns[5] hexInt], instruction.simm()); \ } break; ArithImm(dozi); ArithImm(addi); ArithImm(addic); ArithImm(addic_); ArithImm(addis); ArithImm(mulli); ArithImm(subfic); #undef ArithImm #define LogicImm(x) \ case Operation::x: { \ AssertEqualOperationName(operation, @#x); \ AssertEqualR(columns[3], instruction.rA()); \ AssertEqualR(columns[4], instruction.rS()); \ XCTAssertEqual([columns[5] hexInt], instruction.uimm()); \ } break; LogicImm(andi_); LogicImm(andis_); LogicImm(ori); LogicImm(oris); LogicImm(xori); LogicImm(xoris); #undef ArithImm #define DAB(x) \ case Operation::x: \ AssertEqualOperationName(operation, @#x); \ AssertEqualR(columns[3], instruction.rD()); \ AssertEqualR(columns[4], instruction.rA(), false); \ AssertEqualR(columns[5], instruction.rB()); \ break; DAB(lwzx); DAB(lwzux); DAB(lbzx); DAB(lbzux); DAB(lhzx); DAB(lhzux); DAB(lhax); DAB(lhaux); DAB(lhbrx); DAB(lwbrx); DAB(lwarx); DAB(eciwx); DAB(lswx); DAB(lwa); DAB(lwaux); DAB(lwax); #undef DAB #define DAe(x) \ case Operation::x: \ AssertEqualOperationNameOE(operation, @#x, instruction); \ AssertEqualR(columns[3], instruction.rD()); \ AssertEqualR(columns[4], instruction.rA()); \ break; DAe(negx); DAe(subfzex); DAe(subfmex); DAe(absx); DAe(nabsx); DAe(addzex); DAe(addmex); #undef DAe #define DABe(x) \ case Operation::x: \ AssertEqualOperationNameOE(operation, @#x, instruction); \ AssertEqualR(columns[3], instruction.rD()); \ AssertEqualR(columns[4], instruction.rA()); \ AssertEqualR(columns[5], instruction.rB()); \ break; DABe(subfcx); DABe(subfx); DABe(subfex); DABe(dozx); DABe(addx); DABe(addcx); DABe(addex); DABe(mulhwx); DABe(mulhwux); DABe(mulx); DABe(mullwx); DABe(divx); DABe(divsx); DABe(divwux); DABe(divwx); DABe(lscbxx); #undef DABe #define ASB(x) \ case Operation::x: \ AssertEqualOperationNameE(operation, @#x, instruction); \ AssertEqualR(columns[3], instruction.rA()); \ AssertEqualR(columns[4], instruction.rS()); \ AssertEqualR(columns[5], instruction.rB()); \ break; ASB(maskgx); ASB(maskirx); ASB(rribx); ASB(slex); ASB(sleqx); ASB(sllqx); ASB(slqx); ASB(sraqx); ASB(srex); ASB(sreax); ASB(sreqx); ASB(srlqx); ASB(srqx); ASB(andx); ASB(andcx); ASB(norx); ASB(eqvx); ASB(xorx); ASB(orcx); ASB(orx); ASB(nandx); #undef ASB #define SAB(x) \ case Operation::x: \ AssertEqualOperationName(operation, @#x); \ AssertEqualR(columns[3], instruction.rS()); \ AssertEqualR(columns[4], instruction.rA(), false); \ AssertEqualR(columns[5], instruction.rB()); \ break; SAB(ecowx); SAB(stbux); SAB(stbx); SAB(sthbrx); SAB(sthux); SAB(sthx); SAB(stswx); SAB(stwbrx); SAB(stwcx_); SAB(stwux); SAB(stwx); SAB(stdcx_); SAB(stdux); SAB(stdx); #undef SAB #define AS(x) \ case Operation::x: \ AssertEqualOperationNameE(operation, @#x, instruction); \ AssertEqualR(columns[3], instruction.rA()); \ AssertEqualR(columns[4], instruction.rS()); \ break; AS(cntlzwx); AS(extsbx); AS(extshx); AS(extswx); #undef AS #define fSAB(x) \ case Operation::x: \ AssertEqualOperationName(operation, @#x, instruction); \ AssertEqualFR(columns[3], instruction.frS()); \ AssertEqualR(columns[4], instruction.rA(), false); \ AssertEqualR(columns[5], instruction.rB()); \ break; fSAB(stfdx); fSAB(stfdux); fSAB(stfsx); fSAB(stfsux); fSAB(stfiwx); #undef fSAB #define fDAB(x) \ case Operation::x: \ AssertEqualOperationName(operation, @#x, instruction); \ AssertEqualFR(columns[3], instruction.frD()); \ AssertEqualR(columns[4], instruction.rA(), false); \ AssertEqualR(columns[5], instruction.rB()); \ break; fDAB(lfdux); fDAB(lfdx); fDAB(lfsux); fDAB(lfsx); #undef fDAB #define fDfAfB(x) \ case Operation::x: \ AssertEqualOperationNameE(operation, @#x, instruction); \ AssertEqualFR(columns[3], instruction.frD()); \ AssertEqualFR(columns[4], instruction.frA(), false); \ AssertEqualFR(columns[5], instruction.frB()); \ break; fDfAfB(faddx); fDfAfB(faddsx); fDfAfB(fsubx); fDfAfB(fsubsx); fDfAfB(fdivx); fDfAfB(fdivsx); #undef fDfAfB #define fDfB(x) \ case Operation::x: \ AssertEqualOperationNameE(operation, @#x, instruction); \ AssertEqualFR(columns[3], instruction.frD()); \ AssertEqualFR(columns[4], instruction.frB()); \ break; fDfB(fabsx); fDfB(fnabsx); fDfB(fnegx); fDfB(frsqrtex); fDfB(frspx); fDfB(fctiwx); fDfB(fctiwzx); fDfB(fctidx); fDfB(fctidzx); fDfB(fcfidx); fDfB(fresx); fDfB(fmrx); #undef fDfB #define fDfAfC(x) \ case Operation::x: \ AssertEqualOperationNameE(operation, @#x, instruction); \ AssertEqualFR(columns[3], instruction.frD()); \ AssertEqualFR(columns[4], instruction.frA(), false); \ AssertEqualFR(columns[5], instruction.frC()); \ break; fDfAfC(fmulx); fDfAfC(fmulsx); #undef fDfAfC #define fDfAfCfB(x) \ case Operation::x: \ AssertEqualOperationNameE(operation, @#x, instruction); \ AssertEqualFR(columns[3], instruction.frD()); \ AssertEqualFR(columns[4], instruction.frA()); \ AssertEqualFR(columns[5], instruction.frC()); \ AssertEqualFR(columns[6], instruction.frB()); \ break; fDfAfCfB(fnmaddx); fDfAfCfB(fnmaddsx); fDfAfCfB(fnmsubx); fDfAfCfB(fnmsubsx); fDfAfCfB(fmaddx); fDfAfCfB(fmaddsx); fDfAfCfB(fmsubx); fDfAfCfB(fmsubsx); fDfAfCfB(fselx); #undef fDfAfBfC #define DDA(x) \ case Operation::x: { \ AssertEqualOperationName(operation, @#x, instruction); \ AssertEqualR(columns[3], instruction.rD()); \ XCTAssertEqualObjects(columns[4], offset(instruction)); \ } break; DDA(lbz); DDA(lbzu); DDA(lmw); DDA(lwz); DDA(lwzu); DDA(lhz); DDA(lhzu); DDA(lha); DDA(lhau); #undef DDA #define fDDA(x) \ case Operation::x: { \ AssertEqualOperationName(operation, @#x, instruction); \ AssertEqualFR(columns[3], instruction.rD()); \ XCTAssertEqualObjects(columns[4], offset(instruction)); \ } break; fDDA(lfd); fDDA(lfdu); fDDA(lfs); fDDA(lfsu); #undef fDDA #define SDA(x) \ case Operation::x: { \ AssertEqualOperationName(operation, @#x, instruction); \ AssertEqualR(columns[3], instruction.rS()); \ XCTAssertEqualObjects(columns[4], offset(instruction)); \ } break; SDA(stb); SDA(stbu); SDA(sth); SDA(sthu); SDA(stmw); SDA(stw); SDA(stwu); #undef SDA #define fSDA(x) \ case Operation::x: { \ AssertEqualOperationName(operation, @#x, instruction); \ AssertEqualFR(columns[3], instruction.rS()); \ XCTAssertEqualObjects(columns[4], offset(instruction)); \ } break; fSDA(stfd); fSDA(stfdu); fSDA(stfs); fSDA(stfsu); #undef fSDA #define crfDS(x) \ case Operation::x: \ AssertEqualOperationName(operation, @#x, instruction); \ XCTAssertEqualObjects(columns[3], conditionreg(instruction.crfD())); \ XCTAssertEqualObjects(columns[4], conditionreg(instruction.crfS())); \ break; crfDS(mcrf); crfDS(mcrfs); #undef crfDS #define crfDfAfB(x) \ case Operation::x: \ AssertEqualOperationName(operation, @#x, instruction); \ XCTAssertEqualObjects(columns[3], conditionreg(instruction.crfD())); \ AssertEqualFR(columns[4], instruction.frA()); \ AssertEqualFR(columns[5], instruction.frB()); \ break; crfDfAfB(fcmpo); crfDfAfB(fcmpu); #undef crfDfAfB case Operation::bcx: case Operation::bclrx: case Operation::bcctrx: { NSString *baseOperation = nil; BOOL addConditionToOperand = NO; switch(instruction.branch_options()) { case BranchOption::Always: baseOperation = @"b"; break; case BranchOption::Dec_Zero: baseOperation = @"bdz"; break; case BranchOption::Dec_NotZero: baseOperation = @"bdnz"; break; case BranchOption::Clear: switch(Condition(instruction.bi() & 3)) { default: break; case Condition::Negative: baseOperation = @"bge"; break; case Condition::Positive: baseOperation = @"ble"; break; case Condition::Zero: baseOperation = @"bne"; break; case Condition::SummaryOverflow: baseOperation = @"bns"; break; } break; case BranchOption::Dec_ZeroAndClear: baseOperation = @"bdzf"; addConditionToOperand = YES; break; case BranchOption::Dec_NotZeroAndClear: baseOperation = @"bdnzf"; addConditionToOperand = YES; break; case BranchOption::Set: switch(Condition(instruction.bi() & 3)) { default: break; case Condition::Negative: baseOperation = @"blt"; break; case Condition::Positive: baseOperation = @"bgt"; break; case Condition::Zero: baseOperation = @"beq"; break; case Condition::SummaryOverflow: baseOperation = @"bso"; break; } break; case BranchOption::Dec_ZeroAndSet: baseOperation = @"bdzt"; addConditionToOperand = YES; break; case BranchOption::Dec_NotZeroAndSet: baseOperation = @"bdnzt"; addConditionToOperand = YES; break; default: break; } switch(instruction.operation) { case Operation::bcctrx: baseOperation = [baseOperation stringByAppendingString:@"ctr"]; break; case Operation::bclrx: baseOperation = [baseOperation stringByAppendingString:@"lr"]; break; case Operation::bcx: { uint32_t decoded_destination; if(instruction.aa()) { decoded_destination = instruction.bd(); } else { decoded_destination = instruction.bd() + address; } XCTAssertEqual(decoded_destination, [[columns lastObject] hexInt]); } 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.aa()) { baseOperation = [baseOperation stringByAppendingString:@"a"]; } if(instruction.branch_prediction_hint()) { baseOperation = [baseOperation stringByAppendingString:@"+"]; } AssertEqualOperationName(operation, baseOperation); } if(instruction.bi() & ~3) { NSString *expectedCR; if(addConditionToOperand) { expectedCR = condition(instruction.bi()); } else { expectedCR = [NSString stringWithFormat:@"cr%d", instruction.bi() >> 2]; } XCTAssertEqualObjects(columns[3], expectedCR); } } break; case Operation::bx: { switch((instruction.aa() ? 2 : 0) | (instruction.lk() ? 1 : 0)) { 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 decoded_destination = instruction.li() + (instruction.aa() ? 0 : address); XCTAssertEqual(decoded_destination, [columns[3] hexInt]); } break; } } } @end