diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index b7f0763ee..dfbcfd72d 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -496,6 +496,41 @@ void mul(IntT &destination_high, IntT &destination_low, IntT source, Status &sta status.overflow = status.carry = destination_high; } +template +void imul(IntT &destination_high, IntT &destination_low, IntT source, Status &status) { + /* + (as modified by https://www.felixcloutier.com/x86/daa ...) + + IF (OperandSize = 8) + THEN + AX ← AL ∗ SRC (* signed multiplication *) + IF (AX = SignExtend(AL)) + THEN CF = 0; OF = 0; + ELSE CF = 1; OF = 1; + FI; + ELSE IF OperandSize = 16 + THEN + DX:AX ← AX ∗ SRC (* signed multiplication *) + IF (DX:AX = SignExtend(AX)) + THEN CF = 0; OF = 0; + ELSE CF = 1; OF = 1; + FI; + ELSE (* OperandSize = 32 *) + EDX:EAX ← EAX ∗ SRC (* signed multiplication *) + IF (EDX:EAX = SignExtend(EAX)) + THEN CF = 0; OF = 0; + ELSE CF = 1; OF = 1; + FI; + FI; + */ + using sIntT = typename std::make_signed::type; + destination_high = (sIntT(destination_low) * sIntT(source)) >> (8 * sizeof(IntT)); + destination_low = IntT(sIntT(destination_low) * sIntT(source)); + + const auto sign_extension = (destination_low & top_bit()) ? IntT(~0) : 0; + status.overflow = status.carry = destination_high != sign_extension; +} + template void and_(IntT &destination, IntT source, Status &status) { /* @@ -671,6 +706,15 @@ template < Primitive::mul(registers.edx(), registers.eax(), source(), status); } return; + case Operation::IMUL_1: + if constexpr (data_size == DataSize::Byte) { + Primitive::imul(registers.ah(), registers.al(), source(), status); + } else if constexpr (data_size == DataSize::Word) { + Primitive::imul(registers.dx(), registers.ax(), source(), status); + } else if constexpr (data_size == DataSize::DWord) { + Primitive::imul(registers.edx(), registers.eax(), source(), status); + } + return; case Operation::AND: Primitive::and_(destination(), source(), status); break; diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index 67a435cbb..d948fd88f 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -71,7 +71,7 @@ enum class Operation: uint8_t { SUB, /// Unsigned multiply; multiplies the source value by EAX, AX or AL, storing the result in EDX:EAX, DX:AX or AX. MUL, - /// Single operand signed multiply; multiplies the source value by AX or AL, storing the result in DX:AX or AX. + /// Single operand signed multiply; multiplies the source value by EAX, AX or AL, storing the result in EDX:EAX, DX:AX or AX. IMUL_1, /// Unsigned divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. DIV, diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index 1a741a5b1..d9bf0bdaa 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -278,56 +278,59 @@ struct FailedExecution { - (NSArray *)testFiles { NSString *path = [NSString stringWithUTF8String:TestSuiteHome]; NSSet *allowList = [NSSet setWithArray:@[ -// @"37.json.gz", // AAA -// @"3F.json.gz", // AAS -// @"D4.json.gz", // AAM -// @"D5.json.gz", // AAD -// @"27.json.gz", // DAA -// @"2F.json.gz", // DAS -// -// @"98.json.gz", // CBW -// @"99.json.gz", // CWD -// -// // ESC -// @"D8.json.gz", @"D9.json.gz", @"DA.json.gz", @"DB.json.gz", -// @"DC.json.gz", @"DD.json.gz", @"DE.json.gz", @"DE.json.gz", -// -// // Untested: HLT, WAIT -//// -// // ADC -// @"10.json.gz", @"11.json.gz", @"12.json.gz", @"13.json.gz", @"14.json.gz", @"15.json.gz", -// @"80.2.json.gz", @"81.2.json.gz", @"83.2.json.gz", -// -// // ADD -// @"00.json.gz", @"01.json.gz", @"02.json.gz", @"03.json.gz", @"04.json.gz", @"05.json.gz", -// @"80.0.json.gz", @"81.0.json.gz", @"83.0.json.gz", -// -// // SBB -// @"18.json.gz", @"19.json.gz", @"1A.json.gz", @"1B.json.gz", @"1C.json.gz", @"1D.json.gz", -// @"80.3.json.gz", @"81.3.json.gz", @"83.3.json.gz", -// -// // SUB -// @"28.json.gz", @"29.json.gz", @"2A.json.gz", @"2B.json.gz", @"2C.json.gz", @"2D.json.gz", -// @"80.5.json.gz", @"81.5.json.gz", @"83.5.json.gz", + @"37.json.gz", // AAA + @"3F.json.gz", // AAS + @"D4.json.gz", // AAM + @"D5.json.gz", // AAD + @"27.json.gz", // DAA + @"2F.json.gz", // DAS + + @"98.json.gz", // CBW + @"99.json.gz", // CWD + + // ESC + @"D8.json.gz", @"D9.json.gz", @"DA.json.gz", @"DB.json.gz", + @"DC.json.gz", @"DD.json.gz", @"DE.json.gz", @"DE.json.gz", + + // Untested: HLT, WAIT + + // ADC + @"10.json.gz", @"11.json.gz", @"12.json.gz", @"13.json.gz", @"14.json.gz", @"15.json.gz", + @"80.2.json.gz", @"81.2.json.gz", @"83.2.json.gz", + + // ADD + @"00.json.gz", @"01.json.gz", @"02.json.gz", @"03.json.gz", @"04.json.gz", @"05.json.gz", + @"80.0.json.gz", @"81.0.json.gz", @"83.0.json.gz", + + // SBB + @"18.json.gz", @"19.json.gz", @"1A.json.gz", @"1B.json.gz", @"1C.json.gz", @"1D.json.gz", + @"80.3.json.gz", @"81.3.json.gz", @"83.3.json.gz", + + // SUB + @"28.json.gz", @"29.json.gz", @"2A.json.gz", @"2B.json.gz", @"2C.json.gz", @"2D.json.gz", + @"80.5.json.gz", @"81.5.json.gz", @"83.5.json.gz", // MUL @"F6.4.json.gz", @"F7.4.json.gz", -// // NOP -// @"90.json.gz", + // IMUL_1 + @"F6.5.json.gz", @"F7.5.json.gz", + + // NOP + @"90.json.gz", // AND -// @"20.json.gz", @"21.json.gz", @"22.json.gz", @"23.json.gz", @"24.json.gz", @"25.json.gz", -// @"80.4.json.gz", @"81.4.json.gz", @"83.4.json.gz", -// -// // CALL -// @"E8.json.gz", @"FF.2.json.gz", -// @"9A.json.gz", @"FF.3.json.gz", -// -// @"F8.json.gz", // CLC -// @"FC.json.gz", // CLD -// @"FA.json.gz", // CLI -// @"F5.json.gz", // CMC + @"20.json.gz", @"21.json.gz", @"22.json.gz", @"23.json.gz", @"24.json.gz", @"25.json.gz", + @"80.4.json.gz", @"81.4.json.gz", @"83.4.json.gz", + + // CALL + @"E8.json.gz", @"FF.2.json.gz", + @"9A.json.gz", @"FF.3.json.gz", + + @"F8.json.gz", // CLC + @"FC.json.gz", // CLD + @"FA.json.gz", // CLI + @"F5.json.gz", // CMC ]]; NSSet *ignoreList = nil; @@ -585,7 +588,7 @@ struct FailedExecution { - (void)testDecoding { NSMutableArray *failures = [[NSMutableArray alloc] init]; - for(NSString *file in [self testFiles]) { + for(NSString *file in [self testFiles]) @autoreleasepool { for(NSDictionary *test in [self testsInFile:file]) { // A single failure per instruction is fine. if(![self applyDecodingTest:test file:file assert:YES]) { @@ -604,7 +607,7 @@ struct FailedExecution { - (void)testExecution { NSDictionary *metadata = [self metadata]; - for(NSString *file in [self testFiles]) { + for(NSString *file in [self testFiles]) @autoreleasepool { // Determine the metadata key. NSString *const name = [file lastPathComponent]; NSRange first_dot = [name rangeOfString:@"."];