diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index d1e4c0810..2f224fd4d 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -256,7 +256,7 @@ inline void aam(CPU::RegisterPair16 &ax, uint8_t imm, Status &status, FlowContro If ... an immediate value of 0 is used, it will cause a #DE (divide error) exception. */ if(!imm) { - flow_controller.interrupt(Interrupt::DivideByZero); + flow_controller.interrupt(Interrupt::DivideError); return; } @@ -531,6 +531,45 @@ void imul(IntT &destination_high, IntT &destination_low, IntT source, Status &st status.overflow = status.carry = destination_high != sign_extension; } +template +void div(IntT &destination_high, IntT &destination_low, IntT source, FlowControllerT &flow_controller) { + if(!source) { + flow_controller.interrupt(Interrupt::DivideError); + return; + } + + // TEMPORARY HACK. Will not work with DWords. + const uint32_t dividend = (destination_high << (8 * sizeof(IntT))) + destination_low; + const auto result = dividend / source; + if(IntT(result) != result) { + flow_controller.interrupt(Interrupt::DivideError); + return; + } + + destination_low = IntT(result); + destination_high = dividend % source; +} + +template +void idiv(IntT &destination_high, IntT &destination_low, IntT source, FlowControllerT &flow_controller) { + if(!source) { + flow_controller.interrupt(Interrupt::DivideError); + return; + } + + // TEMPORARY HACK. Will not work with DWords. + using sIntT = typename std::make_signed::type; + const int32_t dividend = (sIntT(destination_high) << (8 * sizeof(IntT))) + destination_low; + const auto result = dividend / sIntT(source); + if(sIntT(result) != result) { + flow_controller.interrupt(Interrupt::DivideError); + return; + } + + destination_low = IntT(result); + destination_high = dividend % sIntT(source); +} + template void and_(IntT &destination, IntT source, Status &status) { /* @@ -718,6 +757,24 @@ template < Primitive::imul(registers.edx(), registers.eax(), source(), status); } return; + case Operation::DIV: + if constexpr (data_size == DataSize::Byte) { + Primitive::div(registers.ah(), registers.al(), source(), flow_controller); + } else if constexpr (data_size == DataSize::Word) { + Primitive::div(registers.dx(), registers.ax(), source(), flow_controller); + } else if constexpr (data_size == DataSize::DWord) { + Primitive::div(registers.edx(), registers.eax(), source(), flow_controller); + } + return; + case Operation::IDIV: + if constexpr (data_size == DataSize::Byte) { + Primitive::idiv(registers.ah(), registers.al(), source(), flow_controller); + } else if constexpr (data_size == DataSize::Word) { + Primitive::idiv(registers.dx(), registers.ax(), source(), flow_controller); + } else if constexpr (data_size == DataSize::DWord) { + Primitive::idiv(registers.edx(), registers.eax(), source(), flow_controller); + } + return; case Operation::AND: Primitive::and_(destination(), source(), status); break; diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index 9c93e53e2..4900a42b7 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -73,7 +73,7 @@ enum class Operation: uint8_t { MUL, /// 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. + /// Unsigned divide; divide the AX, DX:AX or EDX:AX by the source(), storing the quotient in AL, AX or EAX and the remainder in AH, DX or EDX. DIV, /// Signed divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH. IDIV, diff --git a/InstructionSets/x86/Interrupts.hpp b/InstructionSets/x86/Interrupts.hpp index c8d1dcb68..51366f11d 100644 --- a/InstructionSets/x86/Interrupts.hpp +++ b/InstructionSets/x86/Interrupts.hpp @@ -12,11 +12,11 @@ namespace InstructionSet::x86 { enum Interrupt { - DivideByZero = 0, - SingleStep = 1, - NMI = 2, - OneByte = 3, - OnOverflow = 4, + DivideError = 0, + SingleStep = 1, + NMI = 2, + OneByte = 3, + OnOverflow = 4, }; } diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index c08c7b2ae..0fdfd263c 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -25,7 +25,7 @@ namespace { // The tests themselves are not duplicated in this repository; // provide their real path here. -constexpr char TestSuiteHome[] = "/Users/thomasharte/Projects/ProcessorTests/8088/v1"; +constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1"; using Status = InstructionSet::x86::Status; struct Registers { @@ -316,7 +316,12 @@ struct FailedExecution { // IMUL_1 @"F6.5.json.gz", @"F7.5.json.gz", - // TODO: DIV, IDIV + // DIV + @"F6.6.json.gz", @"F7.6.json.gz", + + // IDIV + @"F6.7.json.gz", @"F7.7.json.gz", + // TODO: INC, DEC // TODO: IN, OUT // TODO: JO, JNO, JB, JNB, JZ, JNZ, JBE, JNBE, JS, JNS, JP, JNP, JL, JNL, JLE, JNLE, @@ -363,6 +368,7 @@ struct FailedExecution { // TODO: CMP, TEST // TODO: XCHG, XLAT // TODO: SALC, SETMO, SETMOC + ]]; NSSet *ignoreList = nil; @@ -537,8 +543,10 @@ struct FailedExecution { for(NSArray *ram in final_state[@"ram"]) { execution_support.memory.touch([ram[0] intValue]); } - [self populate:execution_support.registers status:initial_status value:initial_state[@"regs"]]; + Registers initial_registers; + [self populate:initial_registers status:initial_status value:initial_state[@"regs"]]; execution_support.status = initial_status; + execution_support.registers = initial_registers; // Execute instruction. execution_support.registers.ip_ += decoded.first;