From e13b4ab7c9cde90d06acab686691429d320dcca5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 28 Feb 2025 21:51:44 -0500 Subject: [PATCH 1/4] Reduce reliance upon macros. --- InstructionSets/x86/Decoder.cpp | 871 +++++++++++++++----------------- InstructionSets/x86/Decoder.hpp | 142 ++++++ 2 files changed, 558 insertions(+), 455 deletions(-) diff --git a/InstructionSets/x86/Decoder.cpp b/InstructionSets/x86/Decoder.cpp index 7f5507c51..0e72476e7 100644 --- a/InstructionSets/x86/Decoder.cpp +++ b/InstructionSets/x86/Decoder.cpp @@ -34,96 +34,8 @@ std::pair::InstructionT> Decoder::decode( // MARK: - Prefixes (if present) and the opcode. -/// Sets the operation and verifies that the current repetition, if any, is compatible, discarding it otherwise. -#define SetOperation(op) \ - operation_ = rep_operation(op, repetition_); - -/// Helper macro for those that follow. -#define SetOpSrcDestSize(op, src, dest, size) \ - SetOperation(Operation::op); \ - source_ = Source::src; \ - destination_ = Source::dest; \ - operation_size_ = size - -/// Covers anything which is complete as soon as the opcode is encountered. -#define Complete(op, src, dest, size) \ - SetOpSrcDestSize(op, src, dest, size); \ - phase_ = Phase::ReadyToPost - -/// Handles instructions of the form rr, kk and rr, jjkk, i.e. a destination register plus an operand. -#define RegData(op, dest, size) \ - SetOpSrcDestSize(op, DirectAddress, dest, size); \ - source_ = Source::Immediate; \ - operand_size_ = size; \ - phase_ = Phase::DisplacementOrOperand - -/// Handles instructions of the form Ax, jjkk where the latter is implicitly an address. -#define RegAddr(op, dest, op_size, addr_size) \ - SetOpSrcDestSize(op, DirectAddress, dest, op_size); \ - displacement_size_ = addr_size; \ - phase_ = Phase::DisplacementOrOperand; \ - sign_extend_displacement_ = false - -/// Handles instructions of the form jjkk, Ax where the former is implicitly an address. -#define AddrReg(op, source, op_size, addr_size) \ - SetOpSrcDestSize(op, source, DirectAddress, op_size); \ - displacement_size_ = addr_size; \ - destination_ = Source::DirectAddress; \ - phase_ = Phase::DisplacementOrOperand; \ - sign_extend_displacement_ = false - -/// Covers both `mem/reg, reg` and `reg, mem/reg`. -#define MemRegReg(op, format, size) \ - SetOperation(Operation::op); \ - phase_ = Phase::ModRegRM; \ - modregrm_format_ = ModRegRMFormat::format; \ - operand_size_ = DataSize::None; \ - operation_size_ = size - -/// Handles JO, JNO, JB, etc — anything with only a displacement. -#define Displacement(op, size) \ - SetOperation(Operation::op); \ - phase_ = Phase::DisplacementOrOperand; \ - operation_size_= displacement_size_ = size - -/// Handles PUSH [immediate], etc — anything with only an immediate operand. -#define Immediate(op, size) \ - SetOperation(Operation::op); \ - source_ = Source::Immediate; \ - phase_ = Phase::DisplacementOrOperand; \ - operand_size_ = size - -/// Handles far CALL and far JMP — fixed four or six byte operand operations. -#define Far(op) \ - SetOperation(Operation::op); \ - phase_ = Phase::DisplacementOrOperand; \ - operation_size_ = operand_size_ = DataSize::Word; \ - destination_ = Source::Immediate; \ - displacement_size_ = data_size(default_address_size_) - -/// Handles ENTER — a fixed three-byte operation. -#define Displacement16Operand8(op) \ - SetOperation(Operation::op); \ - phase_ = Phase::DisplacementOrOperand; \ - displacement_size_ = DataSize::Word; \ - operand_size_ = DataSize::Byte - -/// Sets up the operation size, oncoming phase and modregrm format for a member of the shift group (i.e. 'group 2'). -#define ShiftGroup() { \ - const DataSize sizes[] = {DataSize::Byte, data_size_}; \ - phase_ = Phase::ModRegRM; \ - modregrm_format_ = ModRegRMFormat::MemRegROL_to_SAR; \ - operation_size_ = sizes[instr & 1]; \ -} - -#define undefined() { \ - const auto result = std::make_pair(consumed_, InstructionT()); \ - reset_parsing(); \ - return result; \ -} - -#define Requires(x) if constexpr (model != Model::x) undefined(); -#define RequiresMin(x) if constexpr (model < Model::x) undefined(); +#define Requires(x) if constexpr (model != Model::x) return undefined(); +#define RequiresMin(x) if constexpr (model < Model::x) return undefined(); while(phase_ == Phase::Instruction && source != end) { const uint8_t instr = *source; @@ -131,107 +43,107 @@ std::pair::InstructionT> Decoder::decode( ++consumed_; switch(instr) { - default: undefined(); + default: return undefined(); -#define PartialBlock(start, operation) \ - case start + 0x00: MemRegReg(operation, MemReg_Reg, DataSize::Byte); break; \ - case start + 0x01: MemRegReg(operation, MemReg_Reg, data_size_); break; \ - case start + 0x02: MemRegReg(operation, Reg_MemReg, DataSize::Byte); break; \ - case start + 0x03: MemRegReg(operation, Reg_MemReg, data_size_); break; \ - case start + 0x04: RegData(operation, eAX, DataSize::Byte); break; \ - case start + 0x05: RegData(operation, eAX, data_size_) +#define PartialBlock(start, operation) \ + case start + 0x00: mem_reg_reg(operation, ModRegRMFormat::MemReg_Reg, DataSize::Byte); break; \ + case start + 0x01: mem_reg_reg(operation, ModRegRMFormat::MemReg_Reg, data_size_); break; \ + case start + 0x02: mem_reg_reg(operation, ModRegRMFormat::Reg_MemReg, DataSize::Byte); break; \ + case start + 0x03: mem_reg_reg(operation, ModRegRMFormat::Reg_MemReg, data_size_); break; \ + case start + 0x04: reg_data(operation, Source::eAX, DataSize::Byte); break; \ + case start + 0x05: reg_data(operation, Source::eAX, data_size_); break; - PartialBlock(0x00, ADD); break; - case 0x06: Complete(PUSH, ES, None, data_size_); break; - case 0x07: Complete(POP, None, ES, data_size_); break; + PartialBlock(0x00, Operation::ADD); + case 0x06: complete(Operation::PUSH, Source::ES, Source::None, data_size_); break; + case 0x07: complete(Operation::POP, Source::None, Source::ES, data_size_); break; - PartialBlock(0x08, OR); break; - case 0x0e: Complete(PUSH, CS, None, data_size_); break; + PartialBlock(0x08, Operation::OR); + case 0x0e: complete(Operation::PUSH, Source::CS, Source::None, data_size_); break; // The 286 onwards have a further set of instructions // prefixed with $0f. case 0x0f: if constexpr (model < Model::i80286) { - Complete(POP, None, CS, data_size_); + complete(Operation::POP, Source::None, Source::CS, data_size_); } else { phase_ = Phase::InstructionPageF; } break; - PartialBlock(0x10, ADC); break; - case 0x16: Complete(PUSH, SS, None, DataSize::Word); break; - case 0x17: Complete(POP, None, SS, DataSize::Word); break; + PartialBlock(0x10, Operation::ADC); + case 0x16: complete(Operation::PUSH, Source::SS, Source::None, DataSize::Word); break; + case 0x17: complete(Operation::POP, Source::None, Source::SS, DataSize::Word); break; - PartialBlock(0x18, SBB); break; - case 0x1e: Complete(PUSH, DS, None, DataSize::Word); break; - case 0x1f: Complete(POP, None, DS, DataSize::Word); break; + PartialBlock(0x18, Operation::SBB); + case 0x1e: complete(Operation::PUSH, Source::DS, Source::None, DataSize::Word); break; + case 0x1f: complete(Operation::POP, Source::None, Source::DS, DataSize::Word); break; - PartialBlock(0x20, AND); break; - case 0x26: segment_override_ = Source::ES; break; - case 0x27: Complete(DAA, None, None, DataSize::Byte); break; + PartialBlock(0x20, Operation::AND); + case 0x26: segment_override_ = Source::ES; break; + case 0x27: complete(Operation::DAA, Source::None, Source::None, DataSize::Byte); break; - PartialBlock(0x28, SUB); break; - case 0x2e: segment_override_ = Source::CS; break; - case 0x2f: Complete(DAS, None, None, DataSize::Byte); break; + PartialBlock(0x28, Operation::SUB); + case 0x2e: segment_override_ = Source::CS; break; + case 0x2f: complete(Operation::DAS, Source::None, Source::None, DataSize::Byte); break; - PartialBlock(0x30, XOR); break; - case 0x36: segment_override_ = Source::SS; break; - case 0x37: Complete(AAA, None, None, DataSize::Word); break; + PartialBlock(0x30, Operation::XOR); + case 0x36: segment_override_ = Source::SS; break; + case 0x37: complete(Operation::AAA, Source::None, Source::None, DataSize::Word); break; - PartialBlock(0x38, CMP); break; - case 0x3e: segment_override_ = Source::DS; break; - case 0x3f: Complete(AAS, None, None, DataSize::Word); break; + PartialBlock(0x38, Operation::CMP); + case 0x3e: segment_override_ = Source::DS; break; + case 0x3f: complete(Operation::AAS, Source::None, Source::None, DataSize::Word); break; #undef PartialBlock -#define RegisterBlock(start, operation) \ - case start + 0x00: Complete(operation, eAX, eAX, data_size_); break; \ - case start + 0x01: Complete(operation, eCX, eCX, data_size_); break; \ - case start + 0x02: Complete(operation, eDX, eDX, data_size_); break; \ - case start + 0x03: Complete(operation, eBX, eBX, data_size_); break; \ - case start + 0x04: Complete(operation, eSP, eSP, data_size_); break; \ - case start + 0x05: Complete(operation, eBP, eBP, data_size_); break; \ - case start + 0x06: Complete(operation, eSI, eSI, data_size_); break; \ - case start + 0x07: Complete(operation, eDI, eDI, data_size_) +#define RegisterBlock(start, operation) \ + case start + 0x00: complete(operation, Source::eAX, Source::eAX, data_size_); break; \ + case start + 0x01: complete(operation, Source::eCX, Source::eCX, data_size_); break; \ + case start + 0x02: complete(operation, Source::eDX, Source::eDX, data_size_); break; \ + case start + 0x03: complete(operation, Source::eBX, Source::eBX, data_size_); break; \ + case start + 0x04: complete(operation, Source::eSP, Source::eSP, data_size_); break; \ + case start + 0x05: complete(operation, Source::eBP, Source::eBP, data_size_); break; \ + case start + 0x06: complete(operation, Source::eSI, Source::eSI, data_size_); break; \ + case start + 0x07: complete(operation, Source::eDI, Source::eDI, data_size_); break; - RegisterBlock(0x40, INC); break; - RegisterBlock(0x48, DEC); break; - RegisterBlock(0x50, PUSH); break; - RegisterBlock(0x58, POP); break; + RegisterBlock(0x40, Operation::INC); + RegisterBlock(0x48, Operation::DEC); + RegisterBlock(0x50, Operation::PUSH); + RegisterBlock(0x58, Operation::POP); #undef RegisterBlock case 0x60: if constexpr (model < Model::i80186) { - Displacement(JO, DataSize::Byte); + displacement(Operation::JO, DataSize::Byte); } else { - Complete(PUSHA, None, None, data_size_); + complete(Operation::PUSHA, Source::None, Source::None, data_size_); } break; case 0x61: if constexpr (model < Model::i80186) { - Displacement(JNO, DataSize::Byte); + displacement(Operation::JNO, DataSize::Byte); } else { - Complete(POPA, None, None, data_size_); + complete(Operation::POPA, Source::None, Source::None, data_size_); } break; case 0x62: if constexpr (model < Model::i80186) { - Displacement(JB, DataSize::Byte); + displacement(Operation::JB, DataSize::Byte); } else { - MemRegReg(BOUND, Reg_MemReg, data_size_); + mem_reg_reg(Operation::BOUND, ModRegRMFormat::Reg_MemReg, data_size_); } break; case 0x63: if constexpr (model < Model::i80286) { - Displacement(JNB, DataSize::Byte); + displacement(Operation::JNB, DataSize::Byte); } else { - MemRegReg(ARPL, MemReg_Reg, DataSize::Word); + mem_reg_reg(Operation::ARPL, ModRegRMFormat::MemReg_Reg, DataSize::Word); } break; case 0x64: if constexpr (model < Model::i80386) { - Displacement(JZ, DataSize::Byte); + displacement(Operation::JZ, DataSize::Byte); } else { RequiresMin(i80386); segment_override_ = Source::FS; @@ -239,7 +151,7 @@ std::pair::InstructionT> Decoder::decode( break; case 0x65: if constexpr (model < Model::i80286) { - Displacement(JNZ, DataSize::Byte); + displacement(Operation::JNZ, DataSize::Byte); break; } RequiresMin(i80386); @@ -247,7 +159,7 @@ std::pair::InstructionT> Decoder::decode( break; case 0x66: if constexpr (model < Model::i80286) { - Displacement(JBE, DataSize::Byte); + displacement(Operation::JBE, DataSize::Byte); break; } RequiresMin(i80386); @@ -255,7 +167,7 @@ std::pair::InstructionT> Decoder::decode( break; case 0x67: if constexpr (model < Model::i80286) { - Displacement(JNBE, DataSize::Byte); + displacement(Operation::JNBE, DataSize::Byte); break; } RequiresMin(i80386); @@ -263,266 +175,275 @@ std::pair::InstructionT> Decoder::decode( break; case 0x68: if constexpr (model < Model::i80286) { - Displacement(JS, DataSize::Byte); + displacement(Operation::JS, DataSize::Byte); } else { - Immediate(PUSH, data_size_); + immediate(Operation::PUSH, data_size_); operation_size_ = data_size_; } break; case 0x69: if constexpr (model < Model::i80286) { - Displacement(JNS, DataSize::Byte); + displacement(Operation::JNS, DataSize::Byte); } else { - MemRegReg(IMUL_3, Reg_MemReg, data_size_); + mem_reg_reg(Operation::IMUL_3, ModRegRMFormat::Reg_MemReg, data_size_); operand_size_ = data_size_; } break; case 0x6a: if constexpr (model < Model::i80286) { - Displacement(JP, DataSize::Byte); + displacement(Operation::JP, DataSize::Byte); } else { - Immediate(PUSH, DataSize::Byte); + immediate(Operation::PUSH, DataSize::Byte); } break; case 0x6b: if constexpr (model < Model::i80286) { - Displacement(JNP, DataSize::Byte); + displacement(Operation::JNP, DataSize::Byte); } else { - MemRegReg(IMUL_3, Reg_MemReg, data_size_); + mem_reg_reg(Operation::IMUL_3, ModRegRMFormat::Reg_MemReg, data_size_); operand_size_ = DataSize::Byte; sign_extend_operand_ = true; } break; case 0x6c: // INSB if constexpr (model < Model::i80186) { - Displacement(JL, DataSize::Byte); + displacement(Operation::JL, DataSize::Byte); } else { - Complete(INS, None, None, DataSize::Byte); + complete(Operation::INS, Source::None, Source::None, DataSize::Byte); } break; case 0x6d: // INSW/INSD if constexpr (model < Model::i80186) { - Displacement(JNL, DataSize::Byte); + displacement(Operation::JNL, DataSize::Byte); } else { - Complete(INS, None, None, data_size_); + complete(Operation::INS, Source::None, Source::None, data_size_); } break; case 0x6e: // OUTSB if constexpr (model < Model::i80186) { - Displacement(JLE, DataSize::Byte); + displacement(Operation::JLE, DataSize::Byte); } else { - Complete(OUTS, None, None, DataSize::Byte); + complete(Operation::OUTS, Source::None, Source::None, DataSize::Byte); } break; case 0x6f: // OUTSW/OUSD if constexpr (model < Model::i80186) { - Displacement(JNLE, DataSize::Byte); + displacement(Operation::JNLE, DataSize::Byte); } else { - Complete(OUTS, None, None, data_size_); + complete(Operation::OUTS, Source::None, Source::None, data_size_); } break; - case 0x70: Displacement(JO, DataSize::Byte); break; - case 0x71: Displacement(JNO, DataSize::Byte); break; - case 0x72: Displacement(JB, DataSize::Byte); break; - case 0x73: Displacement(JNB, DataSize::Byte); break; - case 0x74: Displacement(JZ, DataSize::Byte); break; - case 0x75: Displacement(JNZ, DataSize::Byte); break; - case 0x76: Displacement(JBE, DataSize::Byte); break; - case 0x77: Displacement(JNBE, DataSize::Byte); break; - case 0x78: Displacement(JS, DataSize::Byte); break; - case 0x79: Displacement(JNS, DataSize::Byte); break; - case 0x7a: Displacement(JP, DataSize::Byte); break; - case 0x7b: Displacement(JNP, DataSize::Byte); break; - case 0x7c: Displacement(JL, DataSize::Byte); break; - case 0x7d: Displacement(JNL, DataSize::Byte); break; - case 0x7e: Displacement(JLE, DataSize::Byte); break; - case 0x7f: Displacement(JNLE, DataSize::Byte); break; + case 0x70: displacement(Operation::JO, DataSize::Byte); break; + case 0x71: displacement(Operation::JNO, DataSize::Byte); break; + case 0x72: displacement(Operation::JB, DataSize::Byte); break; + case 0x73: displacement(Operation::JNB, DataSize::Byte); break; + case 0x74: displacement(Operation::JZ, DataSize::Byte); break; + case 0x75: displacement(Operation::JNZ, DataSize::Byte); break; + case 0x76: displacement(Operation::JBE, DataSize::Byte); break; + case 0x77: displacement(Operation::JNBE, DataSize::Byte); break; + case 0x78: displacement(Operation::JS, DataSize::Byte); break; + case 0x79: displacement(Operation::JNS, DataSize::Byte); break; + case 0x7a: displacement(Operation::JP, DataSize::Byte); break; + case 0x7b: displacement(Operation::JNP, DataSize::Byte); break; + case 0x7c: displacement(Operation::JL, DataSize::Byte); break; + case 0x7d: displacement(Operation::JNL, DataSize::Byte); break; + case 0x7e: displacement(Operation::JLE, DataSize::Byte); break; + case 0x7f: displacement(Operation::JNLE, DataSize::Byte); break; - case 0x80: MemRegReg(Invalid, MemRegADD_to_CMP, DataSize::Byte); break; - case 0x81: MemRegReg(Invalid, MemRegADD_to_CMP, data_size_); break; - case 0x82: MemRegReg(Invalid, MemRegADD_to_CMP_SignExtend, DataSize::Byte); break; - case 0x83: MemRegReg(Invalid, MemRegADD_to_CMP_SignExtend, data_size_); break; + case 0x80: + mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegADD_to_CMP, DataSize::Byte); + break; + case 0x81: + mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegADD_to_CMP, data_size_); + break; + case 0x82: + mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegADD_to_CMP_SignExtend, DataSize::Byte); + break; + case 0x83: + mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegADD_to_CMP_SignExtend, data_size_); + break; - case 0x84: MemRegReg(TEST, MemReg_Reg, DataSize::Byte); break; - case 0x85: MemRegReg(TEST, MemReg_Reg, data_size_); break; - case 0x86: MemRegReg(XCHG, Reg_MemReg, DataSize::Byte); break; - case 0x87: MemRegReg(XCHG, Reg_MemReg, data_size_); break; - case 0x88: MemRegReg(MOV, MemReg_Reg, DataSize::Byte); break; - case 0x89: MemRegReg(MOV, MemReg_Reg, data_size_); break; - case 0x8a: MemRegReg(MOV, Reg_MemReg, DataSize::Byte); break; - case 0x8b: MemRegReg(MOV, Reg_MemReg, data_size_); break; - case 0x8c: MemRegReg(MOV, MemReg_Seg, DataSize::Word); break; - case 0x8d: MemRegReg(LEA, Reg_MemReg, data_size_); break; - case 0x8e: MemRegReg(MOV, Seg_MemReg, DataSize::Word); break; - case 0x8f: MemRegReg(POP, MemRegSingleOperand, data_size_); break; + case 0x84: mem_reg_reg(Operation::TEST, ModRegRMFormat::MemReg_Reg, DataSize::Byte); break; + case 0x85: mem_reg_reg(Operation::TEST, ModRegRMFormat::MemReg_Reg, data_size_); break; + case 0x86: mem_reg_reg(Operation::XCHG, ModRegRMFormat::Reg_MemReg, DataSize::Byte); break; + case 0x87: mem_reg_reg(Operation::XCHG, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0x88: mem_reg_reg(Operation::MOV, ModRegRMFormat::MemReg_Reg, DataSize::Byte); break; + case 0x89: mem_reg_reg(Operation::MOV, ModRegRMFormat::MemReg_Reg, data_size_); break; + case 0x8a: mem_reg_reg(Operation::MOV, ModRegRMFormat::Reg_MemReg, DataSize::Byte); break; + case 0x8b: mem_reg_reg(Operation::MOV, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0x8c: mem_reg_reg(Operation::MOV, ModRegRMFormat::MemReg_Seg, DataSize::Word); break; + case 0x8d: mem_reg_reg(Operation::LEA, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0x8e: mem_reg_reg(Operation::MOV, ModRegRMFormat::Seg_MemReg, DataSize::Word); break; + case 0x8f: mem_reg_reg(Operation::POP, ModRegRMFormat::MemRegSingleOperand, data_size_); break; - case 0x90: Complete(NOP, None, None, DataSize::Byte); break; // Could be encoded as XCHG AX, AX if Operation space becomes limited. - case 0x91: Complete(XCHG, eAX, eCX, data_size_); break; - case 0x92: Complete(XCHG, eAX, eDX, data_size_); break; - case 0x93: Complete(XCHG, eAX, eBX, data_size_); break; - case 0x94: Complete(XCHG, eAX, eSP, data_size_); break; - case 0x95: Complete(XCHG, eAX, eBP, data_size_); break; - case 0x96: Complete(XCHG, eAX, eSI, data_size_); break; - case 0x97: Complete(XCHG, eAX, eDI, data_size_); break; + // NOP could be encoded as XCHG AX, AX if Operation space becomes limited. + case 0x90: complete(Operation::NOP, Source::None, Source::None, DataSize::Byte); break; + case 0x91: complete(Operation::XCHG, Source::eAX, Source::eCX, data_size_); break; + case 0x92: complete(Operation::XCHG, Source::eAX, Source::eDX, data_size_); break; + case 0x93: complete(Operation::XCHG, Source::eAX, Source::eBX, data_size_); break; + case 0x94: complete(Operation::XCHG, Source::eAX, Source::eSP, data_size_); break; + case 0x95: complete(Operation::XCHG, Source::eAX, Source::eBP, data_size_); break; + case 0x96: complete(Operation::XCHG, Source::eAX, Source::eSI, data_size_); break; + case 0x97: complete(Operation::XCHG, Source::eAX, Source::eDI, data_size_); break; - case 0x98: Complete(CBW, None, None, data_size_); break; - case 0x99: Complete(CWD, None, None, data_size_); break; - case 0x9a: Far(CALLfar); break; - case 0x9b: Complete(WAIT, None, None, DataSize::Byte); break; - case 0x9c: Complete(PUSHF, None, None, data_size_); break; - case 0x9d: Complete(POPF, None, None, data_size_); break; - case 0x9e: Complete(SAHF, None, None, DataSize::Byte); break; - case 0x9f: Complete(LAHF, None, None, DataSize::Byte); break; + case 0x98: complete(Operation::CBW, Source::None, Source::None, data_size_); break; + case 0x99: complete(Operation::CWD, Source::None, Source::None, data_size_); break; + case 0x9a: far(Operation::CALLfar); break; + case 0x9b: complete(Operation::WAIT, Source::None, Source::None, DataSize::Byte); break; + case 0x9c: complete(Operation::PUSHF, Source::None, Source::None, data_size_); break; + case 0x9d: complete(Operation::POPF, Source::None, Source::None, data_size_); break; + case 0x9e: complete(Operation::SAHF, Source::None, Source::None, DataSize::Byte); break; + case 0x9f: complete(Operation::LAHF, Source::None, Source::None, DataSize::Byte); break; - case 0xa0: RegAddr(MOV, eAX, DataSize::Byte, data_size(address_size_)); break; - case 0xa1: RegAddr(MOV, eAX, data_size_, data_size(address_size_)); break; - case 0xa2: AddrReg(MOV, eAX, DataSize::Byte, data_size(address_size_)); break; - case 0xa3: AddrReg(MOV, eAX, data_size_, data_size(address_size_)); break; + case 0xa0: reg_addr(Operation::MOV, Source::eAX, DataSize::Byte, data_size(address_size_)); break; + case 0xa1: reg_addr(Operation::MOV, Source::eAX, data_size_, data_size(address_size_)); break; + case 0xa2: addr_reg(Operation::MOV, Source::eAX, DataSize::Byte, data_size(address_size_)); break; + case 0xa3: addr_reg(Operation::MOV, Source::eAX, data_size_, data_size(address_size_)); break; - case 0xa4: Complete(MOVS, None, None, DataSize::Byte); break; - case 0xa5: Complete(MOVS, None, None, data_size_); break; - case 0xa6: Complete(CMPS, None, None, DataSize::Byte); break; - case 0xa7: Complete(CMPS, None, None, data_size_); break; - case 0xa8: RegData(TEST, eAX, DataSize::Byte); break; - case 0xa9: RegData(TEST, eAX, data_size_); break; - case 0xaa: Complete(STOS, None, None, DataSize::Byte); break; - case 0xab: Complete(STOS, None, None, data_size_); break; - case 0xac: Complete(LODS, None, None, DataSize::Byte); break; - case 0xad: Complete(LODS, None, None, data_size_); break; - case 0xae: Complete(SCAS, None, None, DataSize::Byte); break; - case 0xaf: Complete(SCAS, None, None, data_size_); break; + case 0xa4: complete(Operation::MOVS, Source::None, Source::None, DataSize::Byte); break; + case 0xa5: complete(Operation::MOVS, Source::None, Source::None, data_size_); break; + case 0xa6: complete(Operation::CMPS, Source::None, Source::None, DataSize::Byte); break; + case 0xa7: complete(Operation::CMPS, Source::None, Source::None, data_size_); break; + case 0xa8: reg_data(Operation::TEST, Source::eAX, DataSize::Byte); break; + case 0xa9: reg_data(Operation::TEST, Source::eAX, data_size_); break; + case 0xaa: complete(Operation::STOS, Source::None, Source::None, DataSize::Byte); break; + case 0xab: complete(Operation::STOS, Source::None, Source::None, data_size_); break; + case 0xac: complete(Operation::LODS, Source::None, Source::None, DataSize::Byte); break; + case 0xad: complete(Operation::LODS, Source::None, Source::None, data_size_); break; + case 0xae: complete(Operation::SCAS, Source::None, Source::None, DataSize::Byte); break; + case 0xaf: complete(Operation::SCAS, Source::None, Source::None, data_size_); break; - case 0xb0: RegData(MOV, eAX, DataSize::Byte); break; - case 0xb1: RegData(MOV, eCX, DataSize::Byte); break; - case 0xb2: RegData(MOV, eDX, DataSize::Byte); break; - case 0xb3: RegData(MOV, eBX, DataSize::Byte); break; - case 0xb4: RegData(MOV, AH, DataSize::Byte); break; - case 0xb5: RegData(MOV, CH, DataSize::Byte); break; - case 0xb6: RegData(MOV, DH, DataSize::Byte); break; - case 0xb7: RegData(MOV, BH, DataSize::Byte); break; - case 0xb8: RegData(MOV, eAX, data_size_); break; - case 0xb9: RegData(MOV, eCX, data_size_); break; - case 0xba: RegData(MOV, eDX, data_size_); break; - case 0xbb: RegData(MOV, eBX, data_size_); break; - case 0xbc: RegData(MOV, eSP, data_size_); break; - case 0xbd: RegData(MOV, eBP, data_size_); break; - case 0xbe: RegData(MOV, eSI, data_size_); break; - case 0xbf: RegData(MOV, eDI, data_size_); break; + case 0xb0: reg_data(Operation::MOV, Source::eAX, DataSize::Byte); break; + case 0xb1: reg_data(Operation::MOV, Source::eCX, DataSize::Byte); break; + case 0xb2: reg_data(Operation::MOV, Source::eDX, DataSize::Byte); break; + case 0xb3: reg_data(Operation::MOV, Source::eBX, DataSize::Byte); break; + case 0xb4: reg_data(Operation::MOV, Source::AH, DataSize::Byte); break; + case 0xb5: reg_data(Operation::MOV, Source::CH, DataSize::Byte); break; + case 0xb6: reg_data(Operation::MOV, Source::DH, DataSize::Byte); break; + case 0xb7: reg_data(Operation::MOV, Source::BH, DataSize::Byte); break; + case 0xb8: reg_data(Operation::MOV, Source::eAX, data_size_); break; + case 0xb9: reg_data(Operation::MOV, Source::eCX, data_size_); break; + case 0xba: reg_data(Operation::MOV, Source::eDX, data_size_); break; + case 0xbb: reg_data(Operation::MOV, Source::eBX, data_size_); break; + case 0xbc: reg_data(Operation::MOV, Source::eSP, data_size_); break; + case 0xbd: reg_data(Operation::MOV, Source::eBP, data_size_); break; + case 0xbe: reg_data(Operation::MOV, Source::eSI, data_size_); break; + case 0xbf: reg_data(Operation::MOV, Source::eDI, data_size_); break; case 0xc0: if constexpr (model >= Model::i80186) { - ShiftGroup(); + shift_group(instr & 1); source_ = Source::Immediate; operand_size_ = DataSize::Byte; } else { - RegData(RETnear, None, data_size_); + reg_data(Operation::RETnear, Source::None, data_size_); } break; case 0xc1: if constexpr (model >= Model::i80186) { - ShiftGroup(); + shift_group(instr & 1); source_ = Source::Immediate; operand_size_ = data_size_; } else { - Complete(RETnear, None, None, DataSize::Byte); + complete(Operation::RETnear, Source::None, Source::None, DataSize::Byte); } break; - case 0xc2: RegData(RETnear, None, data_size_); break; - case 0xc3: Complete(RETnear, None, None, DataSize::Byte); break; - case 0xc4: MemRegReg(LES, Reg_MemReg, data_size_); break; - case 0xc5: MemRegReg(LDS, Reg_MemReg, data_size_); break; - case 0xc6: MemRegReg(MOV, MemRegMOV, DataSize::Byte); break; - case 0xc7: MemRegReg(MOV, MemRegMOV, data_size_); break; + case 0xc2: reg_data(Operation::RETnear, Source::None, data_size_); break; + case 0xc3: complete(Operation::RETnear, Source::None, Source::None, DataSize::Byte); break; + case 0xc4: mem_reg_reg(Operation::LES, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0xc5: mem_reg_reg(Operation::LDS, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0xc6: mem_reg_reg(Operation::MOV, ModRegRMFormat::MemRegMOV, DataSize::Byte); break; + case 0xc7: mem_reg_reg(Operation::MOV, ModRegRMFormat::MemRegMOV, data_size_); break; case 0xc8: if constexpr (model >= Model::i80186) { - Displacement16Operand8(ENTER); + word_displacement_byte_operand(Operation::ENTER); } else { - RegData(RETfar, None, data_size_); + reg_data(Operation::RETfar, Source::None, data_size_); } break; case 0xc9: if constexpr (model >= Model::i80186) { - Complete(LEAVE, None, None, DataSize::Byte); + complete(Operation::LEAVE, Source::None, Source::None, DataSize::Byte); } else { - Complete(RETfar, None, None, DataSize::Word); + complete(Operation::RETfar, Source::None, Source::None, DataSize::Word); } break; - case 0xca: RegData(RETfar, None, data_size_); break; - case 0xcb: Complete(RETfar, None, None, DataSize::Word); break; + case 0xca: reg_data(Operation::RETfar, Source::None, data_size_); break; + case 0xcb: complete(Operation::RETfar, Source::None, Source::None, DataSize::Word); break; case 0xcc: // Encode INT3 as though it were INT with an // immediate operand of 3. - Complete(INT, Immediate, None, DataSize::Byte); + complete(Operation::INT, Source::Immediate, Source::None, DataSize::Byte); operand_ = 3; break; - case 0xcd: RegData(INT, None, DataSize::Byte); break; - case 0xce: Complete(INTO, None, None, DataSize::Byte); break; - case 0xcf: Complete(IRET, None, None, DataSize::Byte); break; + case 0xcd: reg_data(Operation::INT, Source::None, DataSize::Byte); break; + case 0xce: complete(Operation::INTO, Source::None, Source::None, DataSize::Byte); break; + case 0xcf: complete(Operation::IRET, Source::None, Source::None, DataSize::Byte); break; case 0xd0: case 0xd1: - ShiftGroup(); + shift_group(instr & 1); break; case 0xd2: case 0xd3: - ShiftGroup(); + shift_group(instr & 1); source_ = Source::eCX; break; - case 0xd4: RegData(AAM, eAX, DataSize::Byte); break; - case 0xd5: RegData(AAD, eAX, DataSize::Byte); break; - case 0xd6: Complete(SALC, None, None, DataSize::Byte); break; - case 0xd7: Complete(XLAT, None, None, DataSize::Byte); break; + case 0xd4: reg_data(Operation::AAM, Source::eAX, DataSize::Byte); break; + case 0xd5: reg_data(Operation::AAD, Source::eAX, DataSize::Byte); break; + case 0xd6: complete(Operation::SALC, Source::None, Source::None, DataSize::Byte); break; + case 0xd7: complete(Operation::XLAT, Source::None, Source::None, DataSize::Byte); break; - case 0xd8: MemRegReg(ESC, Reg_MemReg, data_size_); break; - case 0xd9: MemRegReg(ESC, Reg_MemReg, data_size_); break; - case 0xda: MemRegReg(ESC, Reg_MemReg, data_size_); break; - case 0xdb: MemRegReg(ESC, Reg_MemReg, data_size_); break; - case 0xdc: MemRegReg(ESC, Reg_MemReg, data_size_); break; - case 0xdd: MemRegReg(ESC, Reg_MemReg, data_size_); break; - case 0xde: MemRegReg(ESC, Reg_MemReg, data_size_); break; - case 0xdf: MemRegReg(ESC, Reg_MemReg, data_size_); break; + case 0xd8: mem_reg_reg(Operation::ESC, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0xd9: mem_reg_reg(Operation::ESC, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0xda: mem_reg_reg(Operation::ESC, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0xdb: mem_reg_reg(Operation::ESC, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0xdc: mem_reg_reg(Operation::ESC, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0xdd: mem_reg_reg(Operation::ESC, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0xde: mem_reg_reg(Operation::ESC, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0xdf: mem_reg_reg(Operation::ESC, ModRegRMFormat::Reg_MemReg, data_size_); break; - case 0xe0: Displacement(LOOPNE, DataSize::Byte); break; - case 0xe1: Displacement(LOOPE, DataSize::Byte); break; - case 0xe2: Displacement(LOOP, DataSize::Byte); break; - case 0xe3: Displacement(JCXZ, DataSize::Byte); break; + case 0xe0: displacement(Operation::LOOPNE, DataSize::Byte); break; + case 0xe1: displacement(Operation::LOOPE, DataSize::Byte); break; + case 0xe2: displacement(Operation::LOOP, DataSize::Byte); break; + case 0xe3: displacement(Operation::JCXZ, DataSize::Byte); break; - case 0xe4: RegAddr(IN, eAX, DataSize::Byte, DataSize::Byte); break; - case 0xe5: RegAddr(IN, eAX, data_size_, DataSize::Byte); break; - case 0xe6: AddrReg(OUT, eAX, DataSize::Byte, DataSize::Byte); break; - case 0xe7: AddrReg(OUT, eAX, data_size_, DataSize::Byte); break; + case 0xe4: reg_addr(Operation::IN, Source::eAX, DataSize::Byte, DataSize::Byte); break; + case 0xe5: reg_addr(Operation::IN, Source::eAX, data_size_, DataSize::Byte); break; + case 0xe6: addr_reg(Operation::OUT, Source::eAX, DataSize::Byte, DataSize::Byte); break; + case 0xe7: addr_reg(Operation::OUT, Source::eAX, data_size_, DataSize::Byte); break; - case 0xe8: Displacement(CALLrel, data_size(address_size_)); break; - case 0xe9: Displacement(JMPrel, data_size(address_size_)); break; - case 0xea: Far(JMPfar); break; - case 0xeb: Displacement(JMPrel, DataSize::Byte); break; + case 0xe8: displacement(Operation::CALLrel, data_size(address_size_)); break; + case 0xe9: displacement(Operation::JMPrel, data_size(address_size_)); break; + case 0xea: far(Operation::JMPfar); break; + case 0xeb: displacement(Operation::JMPrel, DataSize::Byte); break; - case 0xec: Complete(IN, eDX, eAX, DataSize::Byte); break; - case 0xed: Complete(IN, eDX, eAX, data_size_); break; - case 0xee: Complete(OUT, eAX, eDX, DataSize::Byte); break; - case 0xef: Complete(OUT, eAX, eDX, data_size_); break; + case 0xec: complete(Operation::IN, Source::eDX, Source::eAX, DataSize::Byte); break; + case 0xed: complete(Operation::IN, Source::eDX, Source::eAX, data_size_); break; + case 0xee: complete(Operation::OUT, Source::eAX, Source::eDX, DataSize::Byte); break; + case 0xef: complete(Operation::OUT, Source::eAX, Source::eDX, data_size_); break; case 0xf0: lock_ = true; break; // Unused: 0xf1 case 0xf2: repetition_ = Repetition::RepNE; break; case 0xf3: repetition_ = Repetition::RepE; break; - case 0xf4: Complete(HLT, None, None, DataSize::Byte); break; - case 0xf5: Complete(CMC, None, None, DataSize::Byte); break; - case 0xf6: MemRegReg(Invalid, MemRegTEST_to_IDIV, DataSize::Byte); break; - case 0xf7: MemRegReg(Invalid, MemRegTEST_to_IDIV, data_size_); break; + case 0xf4: complete(Operation::HLT, Source::None, Source::None, DataSize::Byte); break; + case 0xf5: complete(Operation::CMC, Source::None, Source::None, DataSize::Byte); break; + case 0xf6: mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegTEST_to_IDIV, DataSize::Byte); break; + case 0xf7: mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegTEST_to_IDIV, data_size_); break; - case 0xf8: Complete(CLC, None, None, DataSize::Byte); break; - case 0xf9: Complete(STC, None, None, DataSize::Byte); break; - case 0xfa: Complete(CLI, None, None, DataSize::Byte); break; - case 0xfb: Complete(STI, None, None, DataSize::Byte); break; - case 0xfc: Complete(CLD, None, None, DataSize::Byte); break; - case 0xfd: Complete(STD, None, None, DataSize::Byte); break; + case 0xf8: complete(Operation::CLC, Source::None, Source::None, DataSize::Byte); break; + case 0xf9: complete(Operation::STC, Source::None, Source::None, DataSize::Byte); break; + case 0xfa: complete(Operation::CLI, Source::None, Source::None, DataSize::Byte); break; + case 0xfb: complete(Operation::STI, Source::None, Source::None, DataSize::Byte); break; + case 0xfc: complete(Operation::CLD, Source::None, Source::None, DataSize::Byte); break; + case 0xfd: complete(Operation::STD, Source::None, Source::None, DataSize::Byte); break; - case 0xfe: MemRegReg(Invalid, MemRegINC_DEC, DataSize::Byte); break; - case 0xff: MemRegReg(Invalid, MemRegINC_to_PUSH, data_size_); break; + case 0xfe: mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegINC_DEC, DataSize::Byte); break; + case 0xff: mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegINC_to_PUSH, data_size_); break; } } @@ -538,135 +459,177 @@ std::pair::InstructionT> Decoder::decode( // NB: to reach here, the instruction set must be at least // that of an 80286. switch(instr) { - default: undefined(); + default: return undefined(); - case 0x00: MemRegReg(Invalid, MemRegSLDT_to_VERW, data_size_); break; - case 0x01: MemRegReg(Invalid, MemRegSGDT_to_LMSW, data_size_); break; - case 0x02: MemRegReg(LAR, Reg_MemReg, data_size_); break; - case 0x03: MemRegReg(LSL, Reg_MemReg, data_size_); break; + case 0x00: mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegSLDT_to_VERW, data_size_); break; + case 0x01: mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegSGDT_to_LMSW, data_size_); break; + case 0x02: mem_reg_reg(Operation::LAR, ModRegRMFormat::Reg_MemReg, data_size_); break; + case 0x03: mem_reg_reg(Operation::LSL, ModRegRMFormat::Reg_MemReg, data_size_); break; case 0x05: Requires(i80286); - Complete(LOADALL, None, None, DataSize::Byte); + complete(Operation::LOADALL, Source::None, Source::None, DataSize::Byte); break; - case 0x06: Complete(CLTS, None, None, DataSize::Byte); break; + case 0x06: complete(Operation::CLTS, Source::None, Source::None, DataSize::Byte); break; case 0x20: RequiresMin(i80386); - MemRegReg(MOVfromCr, Reg_MemReg, DataSize::DWord); + mem_reg_reg(Operation::MOVfromCr, ModRegRMFormat::Reg_MemReg, DataSize::DWord); break; case 0x21: RequiresMin(i80386); - MemRegReg(MOVfromDr, Reg_MemReg, DataSize::DWord); + mem_reg_reg(Operation::MOVfromDr, ModRegRMFormat::Reg_MemReg, DataSize::DWord); break; case 0x22: RequiresMin(i80386); - MemRegReg(MOVtoCr, Reg_MemReg, DataSize::DWord); + mem_reg_reg(Operation::MOVtoCr, ModRegRMFormat::Reg_MemReg, DataSize::DWord); break; case 0x23: RequiresMin(i80386); - MemRegReg(MOVtoDr, Reg_MemReg, DataSize::DWord); + mem_reg_reg(Operation::MOVtoDr, ModRegRMFormat::Reg_MemReg, DataSize::DWord); break; case 0x24: RequiresMin(i80386); - MemRegReg(MOVfromTr, Reg_MemReg, DataSize::DWord); + mem_reg_reg(Operation::MOVfromTr, ModRegRMFormat::Reg_MemReg, DataSize::DWord); break; case 0x26: RequiresMin(i80386); - MemRegReg(MOVtoTr, Reg_MemReg, DataSize::DWord); + mem_reg_reg(Operation::MOVtoTr, ModRegRMFormat::Reg_MemReg, DataSize::DWord); break; - case 0x70: RequiresMin(i80386); Displacement(JO, data_size_); break; - case 0x71: RequiresMin(i80386); Displacement(JNO, data_size_); break; - case 0x72: RequiresMin(i80386); Displacement(JB, data_size_); break; - case 0x73: RequiresMin(i80386); Displacement(JNB, data_size_); break; - case 0x74: RequiresMin(i80386); Displacement(JZ, data_size_); break; - case 0x75: RequiresMin(i80386); Displacement(JNZ, data_size_); break; - case 0x76: RequiresMin(i80386); Displacement(JBE, data_size_); break; - case 0x77: RequiresMin(i80386); Displacement(JNBE, data_size_); break; - case 0x78: RequiresMin(i80386); Displacement(JS, data_size_); break; - case 0x79: RequiresMin(i80386); Displacement(JNS, data_size_); break; - case 0x7a: RequiresMin(i80386); Displacement(JP, data_size_); break; - case 0x7b: RequiresMin(i80386); Displacement(JNP, data_size_); break; - case 0x7c: RequiresMin(i80386); Displacement(JL, data_size_); break; - case 0x7d: RequiresMin(i80386); Displacement(JNL, data_size_); break; - case 0x7e: RequiresMin(i80386); Displacement(JLE, data_size_); break; - case 0x7f: RequiresMin(i80386); Displacement(JNLE, data_size_); break; + case 0x70: RequiresMin(i80386); displacement(Operation::JO, data_size_); break; + case 0x71: RequiresMin(i80386); displacement(Operation::JNO, data_size_); break; + case 0x72: RequiresMin(i80386); displacement(Operation::JB, data_size_); break; + case 0x73: RequiresMin(i80386); displacement(Operation::JNB, data_size_); break; + case 0x74: RequiresMin(i80386); displacement(Operation::JZ, data_size_); break; + case 0x75: RequiresMin(i80386); displacement(Operation::JNZ, data_size_); break; + case 0x76: RequiresMin(i80386); displacement(Operation::JBE, data_size_); break; + case 0x77: RequiresMin(i80386); displacement(Operation::JNBE, data_size_); break; + case 0x78: RequiresMin(i80386); displacement(Operation::JS, data_size_); break; + case 0x79: RequiresMin(i80386); displacement(Operation::JNS, data_size_); break; + case 0x7a: RequiresMin(i80386); displacement(Operation::JP, data_size_); break; + case 0x7b: RequiresMin(i80386); displacement(Operation::JNP, data_size_); break; + case 0x7c: RequiresMin(i80386); displacement(Operation::JL, data_size_); break; + case 0x7d: RequiresMin(i80386); displacement(Operation::JNL, data_size_); break; + case 0x7e: RequiresMin(i80386); displacement(Operation::JLE, data_size_); break; + case 0x7f: RequiresMin(i80386); displacement(Operation::JNLE, data_size_); break; #define Set(x) \ RequiresMin(i80386); \ - MemRegReg(SET##x, MemRegSingleOperand, DataSize::Byte); + mem_reg_reg(x, ModRegRMFormat::MemRegSingleOperand, DataSize::Byte); - case 0x90: Set(O); break; - case 0x91: Set(NO); break; - case 0x92: Set(B); break; - case 0x93: Set(NB); break; - case 0x94: Set(Z); break; - case 0x95: Set(NZ); break; - case 0x96: Set(BE); break; - case 0x97: Set(NBE); break; - case 0x98: Set(S); break; - case 0x99: Set(NS); break; - case 0x9a: Set(P); break; - case 0x9b: Set(NP); break; - case 0x9c: Set(L); break; - case 0x9d: Set(NL); break; - case 0x9e: Set(LE); break; - case 0x9f: Set(NLE); break; + case 0x90: Set(Operation::SETO); break; + case 0x91: Set(Operation::SETNO); break; + case 0x92: Set(Operation::SETB); break; + case 0x93: Set(Operation::SETNB); break; + case 0x94: Set(Operation::SETZ); break; + case 0x95: Set(Operation::SETNZ); break; + case 0x96: Set(Operation::SETBE); break; + case 0x97: Set(Operation::SETNBE); break; + case 0x98: Set(Operation::SETS); break; + case 0x99: Set(Operation::SETNS); break; + case 0x9a: Set(Operation::SETP); break; + case 0x9b: Set(Operation::SETNP); break; + case 0x9c: Set(Operation::SETL); break; + case 0x9d: Set(Operation::SETNL); break; + case 0x9e: Set(Operation::SETLE); break; + case 0x9f: Set(Operation::SETNLE); break; #undef Set - case 0xa0: RequiresMin(i80386); Complete(PUSH, FS, None, data_size_); break; - case 0xa1: RequiresMin(i80386); Complete(POP, None, FS, data_size_); break; - case 0xa3: RequiresMin(i80386); MemRegReg(BT, MemReg_Reg, data_size_); break; + case 0xa0: + RequiresMin(i80386); + complete(Operation::PUSH, Source::FS, Source::None, data_size_); + break; + case 0xa1: + RequiresMin(i80386); + complete(Operation::POP, Source::None, Source::FS, data_size_); + break; + case 0xa3: + RequiresMin(i80386); + mem_reg_reg(Operation::BT, ModRegRMFormat::MemReg_Reg, data_size_); + break; case 0xa4: RequiresMin(i80386); - MemRegReg(SHLDimm, Reg_MemReg, data_size_); + mem_reg_reg(Operation::SHLDimm, ModRegRMFormat::Reg_MemReg, data_size_); operand_size_ = DataSize::Byte; break; case 0xa5: RequiresMin(i80386); - MemRegReg(SHLDCL, MemReg_Reg, data_size_); + mem_reg_reg(Operation::SHLDCL, ModRegRMFormat::MemReg_Reg, data_size_); + break; + case 0xa8: + RequiresMin(i80386); + complete(Operation::PUSH, Source::GS, Source::None, data_size_); + break; + case 0xa9: + RequiresMin(i80386); + complete(Operation::POP, Source::None, Source::GS, data_size_); + break; + case 0xab: + RequiresMin(i80386); + mem_reg_reg(Operation::BTS, ModRegRMFormat::MemReg_Reg, data_size_); break; - case 0xa8: RequiresMin(i80386); Complete(PUSH, GS, None, data_size_); break; - case 0xa9: RequiresMin(i80386); Complete(POP, None, GS, data_size_); break; - case 0xab: RequiresMin(i80386); MemRegReg(BTS, MemReg_Reg, data_size_); break; case 0xac: RequiresMin(i80386); - MemRegReg(SHRDimm, Reg_MemReg, data_size_); + mem_reg_reg(Operation::SHRDimm, ModRegRMFormat::Reg_MemReg, data_size_); operand_size_ = DataSize::Byte; break; case 0xad: RequiresMin(i80386); - MemRegReg(SHRDCL, MemReg_Reg, data_size_); + mem_reg_reg(Operation::SHRDCL, ModRegRMFormat::MemReg_Reg, data_size_); break; case 0xaf: RequiresMin(i80386); - MemRegReg(IMUL_2, Reg_MemReg, data_size_); + mem_reg_reg(Operation::IMUL_2, ModRegRMFormat::Reg_MemReg, data_size_); break; - case 0xb2: RequiresMin(i80386); MemRegReg(LSS, Reg_MemReg, data_size_); break; - case 0xb3: RequiresMin(i80386); MemRegReg(BTR, MemReg_Reg, data_size_); break; - case 0xb4: RequiresMin(i80386); MemRegReg(LFS, Reg_MemReg, data_size_); break; - case 0xb5: RequiresMin(i80386); MemRegReg(LGS, Reg_MemReg, data_size_); break; + case 0xb2: + RequiresMin(i80386); + mem_reg_reg(Operation::LSS, ModRegRMFormat::Reg_MemReg, data_size_); + break; + case 0xb3: + RequiresMin(i80386); + mem_reg_reg(Operation::BTR, ModRegRMFormat::MemReg_Reg, data_size_); + break; + case 0xb4: + RequiresMin(i80386); + mem_reg_reg(Operation::LFS, ModRegRMFormat::Reg_MemReg, data_size_); + break; + case 0xb5: + RequiresMin(i80386); + mem_reg_reg(Operation::LGS, ModRegRMFormat::Reg_MemReg, data_size_); + break; case 0xb6: RequiresMin(i80386); - MemRegReg(MOVZX, Reg_MemReg, DataSize::Byte); + mem_reg_reg(Operation::MOVZX, ModRegRMFormat::Reg_MemReg, DataSize::Byte); break; case 0xb7: RequiresMin(i80386); - MemRegReg(MOVZX, Reg_MemReg, DataSize::Word); + mem_reg_reg(Operation::MOVZX, ModRegRMFormat::Reg_MemReg, DataSize::Word); + break; + case 0xba: + RequiresMin(i80386); + mem_reg_reg(Operation::Invalid, ModRegRMFormat::MemRegBT_to_BTC, data_size_); + break; + case 0xbb: + RequiresMin(i80386); + mem_reg_reg(Operation::BTC, ModRegRMFormat::MemReg_Reg, data_size_); + break; + case 0xbc: + RequiresMin(i80386); + mem_reg_reg(Operation::BSF, ModRegRMFormat::MemReg_Reg, data_size_); + break; + case 0xbd: + RequiresMin(i80386); + mem_reg_reg(Operation::BSR, ModRegRMFormat::MemReg_Reg, data_size_); break; - case 0xba: RequiresMin(i80386); MemRegReg(Invalid, MemRegBT_to_BTC, data_size_); break; - case 0xbb: RequiresMin(i80386); MemRegReg(BTC, MemReg_Reg, data_size_); break; - case 0xbc: RequiresMin(i80386); MemRegReg(BSF, MemReg_Reg, data_size_); break; - case 0xbd: RequiresMin(i80386); MemRegReg(BSR, MemReg_Reg, data_size_); break; case 0xbe: RequiresMin(i80386); - MemRegReg(MOVSX, Reg_MemReg, DataSize::Byte); + mem_reg_reg(Operation::MOVSX, ModRegRMFormat::Reg_MemReg, DataSize::Byte); break; case 0xbf: RequiresMin(i80386); - MemRegReg(MOVSX, Reg_MemReg, DataSize::Word); + mem_reg_reg(Operation::MOVSX, ModRegRMFormat::Reg_MemReg, DataSize::Word); break; } } @@ -674,17 +637,8 @@ std::pair::InstructionT> Decoder::decode( #undef Requires #undef RequiresMin -#undef ShiftGroup -#undef Displacement16Operand8 #undef Far -#undef Immediate #undef Displacement -#undef MemRegReg -#undef AddrReg -#undef RegAddr -#undef RegData -#undef Complete -#undef SetOpSrcDestSize // MARK: - ModRegRM byte, if any. @@ -701,11 +655,11 @@ std::pair::InstructionT> Decoder::decode( // These tables are fairly redundant due to the register ordering within // Source, but act to improve readability and permit further Source // reordering in the future. - constexpr Source reg_table[8] = { + static constexpr Source reg_table[8] = { Source::eAX, Source::eCX, Source::eDX, Source::eBX, Source::eSPorAH, Source::eBPorCH, Source::eSIorDH, Source::eDIorBH, }; - constexpr Source seg_table[6] = { + static constexpr Source seg_table[6] = { Source::ES, Source::CS, Source::SS, Source::DS, Source::FS, Source::GS }; @@ -720,8 +674,9 @@ std::pair::InstructionT> Decoder::decode( operation_ == Operation::LDS || operation_ == Operation::LGS || operation_ == Operation::LSS || - operation_ == Operation::LFS) { - undefined(); + operation_ == Operation::LFS + ) { + return undefined(); } } else if(rm == 6 && mod == 0) { // There's no BP direct; BP with ostensibly no offset means 'direct address' mode. @@ -748,7 +703,7 @@ std::pair::InstructionT> Decoder::decode( // // A base of eAX is meaningless, with the source type being the indicator // that it should be ignored. ScaleIndexBase can't store a base of Source::None. - constexpr ScaleIndexBase rm_table[8] = { + static constexpr ScaleIndexBase rm_table[8] = { ScaleIndexBase(0, Source::eSI, Source::eBX), ScaleIndexBase(0, Source::eDI, Source::eBX), ScaleIndexBase(0, Source::eSI, Source::eBP), @@ -784,7 +739,7 @@ std::pair::InstructionT> Decoder::decode( // case 1 is treated as another form of TEST on the 8086. // (and, I guess, the 80186?) if constexpr (model >= Model::i80286) { - undefined(); + return undefined(); } [[fallthrough]]; @@ -792,14 +747,14 @@ std::pair::InstructionT> Decoder::decode( destination_ = memreg; source_ = Source::Immediate; operand_size_ = operation_size_; - SetOperation(Operation::TEST); + set(Operation::TEST); break; - case 2: SetOperation(Operation::NOT); break; - case 3: SetOperation(Operation::NEG); break; - case 4: SetOperation(Operation::MUL); break; - case 5: SetOperation(Operation::IMUL_1); break; - case 6: SetOperation(Operation::DIV); break; - case 7: SetOperation(Operation::IDIV); break; + case 2: set(Operation::NOT); break; + case 3: set(Operation::NEG); break; + case 4: set(Operation::MUL); break; + case 5: set(Operation::IMUL_1); break; + case 6: set(Operation::DIV); break; + case 7: set(Operation::IDIV); break; } break; @@ -812,11 +767,11 @@ std::pair::InstructionT> Decoder::decode( // the 80386 onwards has six. if constexpr (is_32bit(model)) { if(masked_reg > 5) { - undefined(); + return undefined(); } } else { if(masked_reg > 3) { - undefined(); + return undefined(); } } @@ -826,7 +781,7 @@ std::pair::InstructionT> Decoder::decode( // 80286 and later disallow MOV to CS. if(model >= Model::i80286 && destination_ == Source::CS) { - undefined(); + return undefined(); } } else { source_ = seg_table[masked_reg]; @@ -841,22 +796,22 @@ std::pair::InstructionT> Decoder::decode( default: if constexpr (model == Model::i8086) { if(source_ == Source::eCX) { - SetOperation(Operation::SETMOC); + set(Operation::SETMOC); } else { - SetOperation(Operation::SETMO); + set(Operation::SETMO); } } else { - undefined(); + return undefined(); } break; - case 0: SetOperation(Operation::ROL); break; - case 1: SetOperation(Operation::ROR); break; - case 2: SetOperation(Operation::RCL); break; - case 3: SetOperation(Operation::RCR); break; - case 4: SetOperation(Operation::SAL); break; - case 5: SetOperation(Operation::SHR); break; - case 7: SetOperation(Operation::SAR); break; + case 0: set(Operation::ROL); break; + case 1: set(Operation::ROR); break; + case 2: set(Operation::RCL); break; + case 3: set(Operation::RCR); break; + case 4: set(Operation::SAL); break; + case 5: set(Operation::SHR); break; + case 7: set(Operation::SAR); break; } break; @@ -864,10 +819,10 @@ std::pair::InstructionT> Decoder::decode( source_ = destination_ = memreg; switch(reg) { - default: undefined(); + default: return undefined(); - case 0: SetOperation(Operation::INC); break; - case 1: SetOperation(Operation::DEC); break; + case 0: set(Operation::INC); break; + case 1: set(Operation::DEC); break; } break; @@ -879,17 +834,17 @@ std::pair::InstructionT> Decoder::decode( // case 7 is treated as another form of PUSH on the 8086. // (and, I guess, the 80186?) if constexpr (model >= Model::i80286) { - undefined(); + return undefined(); } [[fallthrough]]; - case 6: SetOperation(Operation::PUSH); break; + case 6: set(Operation::PUSH); break; - case 0: SetOperation(Operation::INC); break; - case 1: SetOperation(Operation::DEC); break; - case 2: SetOperation(Operation::CALLabs); break; - case 3: SetOperation(Operation::CALLfar); break; - case 4: SetOperation(Operation::JMPabs); break; - case 5: SetOperation(Operation::JMPfar); break; + case 0: set(Operation::INC); break; + case 1: set(Operation::DEC); break; + case 2: set(Operation::CALLabs); break; + case 3: set(Operation::CALLfar); break; + case 4: set(Operation::JMPabs); break; + case 5: set(Operation::JMPfar); break; } break; @@ -897,7 +852,7 @@ std::pair::InstructionT> Decoder::decode( source_ = destination_ = memreg; if(reg != 0) { - undefined(); + return undefined(); } break; @@ -911,18 +866,23 @@ std::pair::InstructionT> Decoder::decode( case ModRegRMFormat::MemRegADD_to_CMP_SignExtend: source_ = Source::Immediate; destination_ = memreg; - operand_size_ = (modregrm_format_ == ModRegRMFormat::MemRegADD_to_CMP_SignExtend) ? DataSize::Byte : operation_size_; - sign_extend_operand_ = true; // Will be effective only if modregrm_format_ == ModRegRMFormat::MemRegADD_to_CMP_SignExtend. + operand_size_ = + (modregrm_format_ == ModRegRMFormat::MemRegADD_to_CMP_SignExtend) + ? DataSize::Byte : operation_size_; + + // sign_extend_operand_ will be effective only if + // modregrm_format_ == ModRegRMFormat::MemRegADD_to_CMP_SignExtend. + sign_extend_operand_ = true; switch(reg) { - default: SetOperation(Operation::ADD); break; - case 1: SetOperation(Operation::OR); break; - case 2: SetOperation(Operation::ADC); break; - case 3: SetOperation(Operation::SBB); break; - case 4: SetOperation(Operation::AND); break; - case 5: SetOperation(Operation::SUB); break; - case 6: SetOperation(Operation::XOR); break; - case 7: SetOperation(Operation::CMP); break; + default: set(Operation::ADD); break; + case 1: set(Operation::OR); break; + case 2: set(Operation::ADC); break; + case 3: set(Operation::SBB); break; + case 4: set(Operation::AND); break; + case 5: set(Operation::SUB); break; + case 6: set(Operation::XOR); break; + case 7: set(Operation::CMP); break; } break; @@ -930,14 +890,14 @@ std::pair::InstructionT> Decoder::decode( destination_ = source_ = memreg; switch(reg) { - default: undefined(); + default: return undefined(); - case 0: SetOperation(Operation::SLDT); break; - case 1: SetOperation(Operation::STR); break; - case 2: SetOperation(Operation::LLDT); break; - case 3: SetOperation(Operation::LTR); break; - case 4: SetOperation(Operation::VERR); break; - case 5: SetOperation(Operation::VERW); break; + case 0: set(Operation::SLDT); break; + case 1: set(Operation::STR); break; + case 2: set(Operation::LLDT); break; + case 3: set(Operation::LTR); break; + case 4: set(Operation::VERR); break; + case 5: set(Operation::VERW); break; } break; @@ -945,14 +905,14 @@ std::pair::InstructionT> Decoder::decode( destination_ = source_ = memreg; switch(reg) { - default: undefined(); + default: return undefined(); - case 0: SetOperation(Operation::SGDT); break; - case 1: SetOperation(Operation::SIDT); break; - case 2: SetOperation(Operation::LGDT); break; - case 3: SetOperation(Operation::LIDT); break; - case 4: SetOperation(Operation::SMSW); break; - case 6: SetOperation(Operation::LMSW); break; + case 0: set(Operation::SGDT); break; + case 1: set(Operation::SIDT); break; + case 2: set(Operation::LGDT); break; + case 3: set(Operation::LIDT); break; + case 4: set(Operation::SMSW); break; + case 6: set(Operation::LMSW); break; } break; @@ -962,12 +922,12 @@ std::pair::InstructionT> Decoder::decode( operand_size_ = DataSize::Byte; switch(reg) { - default: undefined(); + default: return undefined(); - case 4: SetOperation(Operation::BT); break; - case 5: SetOperation(Operation::BTS); break; - case 6: SetOperation(Operation::BTR); break; - case 7: SetOperation(Operation::BTC); break; + case 4: set(Operation::BT); break; + case 5: set(Operation::BTS); break; + case 6: set(Operation::BTR); break; + case 7: set(Operation::BTC); break; } break; @@ -977,13 +937,12 @@ std::pair::InstructionT> Decoder::decode( if(expects_sib && (source_ == Source::Indirect || destination_ == Source::Indirect)) { phase_ = Phase::ScaleIndexBase; } else { - phase_ = (displacement_size_ != DataSize::None || operand_size_ != DataSize::None) ? Phase::DisplacementOrOperand : Phase::ReadyToPost; + phase_ = + (displacement_size_ != DataSize::None || operand_size_ != DataSize::None) + ? Phase::DisplacementOrOperand : Phase::ReadyToPost; } } -#undef undefined -#undef SetOperation - // MARK: - ScaleIndexBase if constexpr (is_32bit(model)) { @@ -998,7 +957,9 @@ std::pair::InstructionT> Decoder::decode( destination_ = (destination_ == Source::Indirect) ? Source::IndirectNoBase : destination_; } - phase_ = (displacement_size_ != DataSize::None || operand_size_ != DataSize::None) ? Phase::DisplacementOrOperand : Phase::ReadyToPost; + phase_ = + (displacement_size_ != DataSize::None || operand_size_ != DataSize::None) + ? Phase::DisplacementOrOperand : Phase::ReadyToPost; } } diff --git a/InstructionSets/x86/Decoder.hpp b/InstructionSets/x86/Decoder.hpp index ab31fef40..1269d66cf 100644 --- a/InstructionSets/x86/Decoder.hpp +++ b/InstructionSets/x86/Decoder.hpp @@ -225,6 +225,148 @@ private: sign_extend_operand_ = false; sign_extend_displacement_ = true; } + + // + // Construction helpers. + // + + /// Sets the operation and verifies that the current repetition, if any, is compatible, discarding it otherwise. + void set(const Operation operation) { + operation_ = rep_operation(operation, repetition_); + }; + + /// Helper for those that follow... + void set( + const Operation operation, + const Source source, + const Source destination, + const DataSize operation_size + ) { + set(operation); + source_ = source; + destination_ = destination; + operation_size_ = operation_size; + }; + + /// Covers anything which is complete as soon as the opcode is encountered. + void complete( + const Operation operation, + const Source source, + const Source destination, + const DataSize operation_size + ) { + set(operation, source, destination, operation_size); + phase_ = Phase::ReadyToPost; + } + + /// Handles instructions of the form rr, kk and rr, jjkk, i.e. a destination register plus an operand. + void reg_data( + const Operation operation, + const Source destination, + const DataSize operation_size + ) { + set(operation, Source::Immediate, destination, operation_size); + operand_size_ = operation_size; + phase_ = Phase::DisplacementOrOperand; + } + + /// Handles instructions of the form Ax, jjkk where the latter is implicitly an address. + void reg_addr( + const Operation operation, + const Source destination, + const DataSize operation_size, + const DataSize address_size + ) { + set(operation, Source::DirectAddress, destination, operation_size); + displacement_size_ = address_size; + phase_ = Phase::DisplacementOrOperand; + sign_extend_displacement_ = false; + } + + /// Handles instructions of the form jjkk, Ax where the former is implicitly an address. + void addr_reg( + const Operation operation, + const Source source, + const DataSize operation_size, + const DataSize address_size + ) { + set(operation, source, Source::DirectAddress, operation_size); + displacement_size_ = address_size; + phase_ = Phase::DisplacementOrOperand; + sign_extend_displacement_ = false; + } + + /// Covers both `mem/reg, reg` and `reg, mem/reg`. + void mem_reg_reg( + const Operation operation, + ModRegRMFormat format, + const DataSize operation_size + ) { + set(operation); + phase_ = Phase::ModRegRM; + modregrm_format_ = format; + operand_size_ = DataSize::None; + operation_size_ = operation_size; + } + + /// Handles JO, JNO, JB, etc — anything with only a displacement. + void displacement( + const Operation operation, + const DataSize operation_size + ) { + set(operation); + phase_ = Phase::DisplacementOrOperand; + operation_size_ = displacement_size_ = operation_size; + } + + /// Handles PUSH [immediate], etc — anything with only an immediate operand. + void immediate( + const Operation operation, + const DataSize operand_size + ) { + set(operation); + source_ = Source::Immediate; + phase_ = Phase::DisplacementOrOperand; + operand_size_ = operand_size; + } + + /// Handles far CALL and far JMP — fixed four or six byte operand operations. + void far( + const Operation operation + ) { + set(operation); + phase_ = Phase::DisplacementOrOperand; + operation_size_ = operand_size_ = DataSize::Word; + destination_ = Source::Immediate; + displacement_size_ = data_size(default_address_size_); + } + + /// Handles ENTER — a fixed three-byte operation. + void word_displacement_byte_operand( + const Operation operation + ) { + set(operation); + phase_ = Phase::DisplacementOrOperand; + displacement_size_ = DataSize::Word; + operand_size_ = DataSize::Byte; + } + + /// Sets up the operation size, oncoming phase and modregrm format for a member of the shift group (i.e. 'group 2'). + void shift_group( + const int size_mask + ) { + const DataSize sizes[] = {DataSize::Byte, data_size_}; + phase_ = Phase::ModRegRM; + modregrm_format_ = ModRegRMFormat::MemRegROL_to_SAR; + operation_size_ = sizes[size_mask]; + } + + /// Constructs an instruction for 'undefined' and returns it. + std::pair::InstructionT> undefined() { + const auto result = std::make_pair(consumed_, InstructionT()); + reset_parsing(); + return result; + } }; // This is a temporary measure; for reasons as-yet unknown, GCC isn't picking up the From efd4a83bd208c505f8619d317c3353edbbf8198e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 28 Feb 2025 21:55:52 -0500 Subject: [PATCH 2/4] Remove dead #undefs. --- InstructionSets/x86/Decoder.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/InstructionSets/x86/Decoder.cpp b/InstructionSets/x86/Decoder.cpp index 0e72476e7..99dfbf140 100644 --- a/InstructionSets/x86/Decoder.cpp +++ b/InstructionSets/x86/Decoder.cpp @@ -637,8 +637,6 @@ std::pair::InstructionT> Decoder::decode( #undef Requires #undef RequiresMin -#undef Far -#undef Displacement // MARK: - ModRegRM byte, if any. From 4843b7f7b809ac656212afadabdf547322654cb2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 1 Mar 2025 22:35:15 -0500 Subject: [PATCH 3/4] Update name expectations. --- OSBindings/Mac/Clock SignalTests/8088Tests.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index 413f52686..f50592051 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -30,7 +30,7 @@ namespace { // The tests themselves are not duplicated in this repository; // provide their real path here. -constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1"; +constexpr char TestSuiteHome[] = "/Users/thomasharte/Projects/8088/v1"; using Flags = InstructionSet::x86::Flags; struct Registers { @@ -479,7 +479,7 @@ struct FailedExecution { } - (NSDictionary *)metadata { - NSString *path = [[NSString stringWithUTF8String:TestSuiteHome] stringByAppendingPathComponent:@"8088.json"]; + NSString *path = [[NSString stringWithUTF8String:TestSuiteHome] stringByAppendingPathComponent:@"metadata.json"]; return [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfGZippedFile:path] options:0 error:nil]; } From ea0799c5468f299f68f7f4a7f6ff38e973c13a29 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 3 Mar 2025 23:17:52 -0500 Subject: [PATCH 4/4] Transition off `printf`. --- Machines/PCCompatible/PCCompatible.cpp | 20 +++++++++++++------- Outputs/Log.hpp | 2 ++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Machines/PCCompatible/PCCompatible.cpp b/Machines/PCCompatible/PCCompatible.cpp index 2d8d28fe0..6381d88a8 100644 --- a/Machines/PCCompatible/PCCompatible.cpp +++ b/Machines/PCCompatible/PCCompatible.cpp @@ -33,6 +33,7 @@ #include "Numeric/RegisterSizes.hpp" #include "Outputs/CRT/CRT.hpp" +#include "Outputs/Log.hpp" #include "Outputs/Speaker/Implementation/LowpassSpeaker.hpp" #include "Storage/Disk/Track/TrackSerialiser.hpp" @@ -50,6 +51,11 @@ #include namespace PCCompatible { +namespace { + +Log::Logger log; + +} using Target = Analyser::Static::PCCompatible::Target; @@ -108,7 +114,7 @@ class FloppyController { using Command = Intel::i8272::Command; switch(decoder_.command()) { default: - printf("TODO: implement FDC command %d\n", uint8_t(decoder_.command())); + log.error().append("TODO: implement FDC command %d\n", uint8_t(decoder_.command())); break; case Command::WriteDeletedData: @@ -605,9 +611,9 @@ class IO { switch(port) { default: if constexpr (std::is_same_v) { - printf("Unhandled out: %02x to %04x\n", value, port); + log.error().append("Unhandled out: %02x to %04x\n", value, port); } else { - printf("Unhandled out: %04x to %04x\n", value, port); + log.error().append("Unhandled out: %04x to %04x\n", value, port); } break; @@ -616,7 +622,7 @@ class IO { // On the XT the NMI can be masked by setting bit 7 on I/O port 0xA0. case 0x00a0: - printf("TODO: NMIs %s\n", (value & 0x80) ? "masked" : "unmasked"); + log.error().append("TODO: NMIs %s\n", (value & 0x80) ? "masked" : "unmasked"); break; case 0x0000: dma_.controller.write<0x0>(uint8_t(value)); break; @@ -689,7 +695,7 @@ class IO { fdc_.set_digital_output(uint8_t(value)); break; case 0x03f4: - printf("TODO: FDC write of %02x at %04x\n", value, port); + log.error().append("TODO: FDC write of %02x at %04x\n", value, port); break; case 0x03f5: fdc_.write(uint8_t(value)); @@ -716,7 +722,7 @@ class IO { template IntT in([[maybe_unused]] uint16_t port) { switch(port) { default: - printf("Unhandled in: %04x\n", port); + log.error().append("Unhandled in: %04x\n", port); break; case 0x0000: return dma_.controller.read<0x0>(); @@ -830,7 +836,7 @@ class FlowController { halted_ = true; } void wait() { - printf("WAIT ????\n"); + log.error().append("WAIT ????\n"); } void repeat_last() { diff --git a/Outputs/Log.hpp b/Outputs/Log.hpp index 4a7f25e79..df647e17a 100644 --- a/Outputs/Log.hpp +++ b/Outputs/Log.hpp @@ -48,6 +48,7 @@ enum class Source { MSX, NCR5380, OpenGL, + PCCompatible, Plus4, PCMTrack, SCC, @@ -122,6 +123,7 @@ constexpr const char *prefix(Source source) { case Source::NCR5380: return "5380"; case Source::OpenGL: return "OpenGL"; case Source::Plus4: return "Plus4"; + case Source::PCCompatible: return "PC"; case Source::PCMTrack: return "PCM Track"; case Source::SCSI: return "SCSI"; case Source::SCC: return "SCC";