URL
https://opencores.org/ocsvn/s80186/s80186/trunk
Subversion Repositories s80186
[/] [s80186/] [trunk/] [sim/] [cppmodel/] [Emulate.cpp] - Rev 2
Compare with Previous | Blame | View Log
// Copyright Jamie Iles, 2017 // // This file is part of s80x86. // // s80x86 is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // s80x86 is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with s80x86. If not, see <http://www.gnu.org/licenses/>. #include "Emulate.h" #include <cassert> #include <cstdlib> #include <iostream> #include <functional> #include <stdint.h> #include "Memory.h" #include "SoftwareCPU.h" #include <config.h> template <typename Out, typename In> static inline Out sign_extend(In v) { static_assert(sizeof(Out) > sizeof(In), "May only sign extend to larger types"); size_t bit_shift = (sizeof(Out) - sizeof(In)) * 8; Out o = static_cast<Out>(v); o <<= bit_shift; o >>= bit_shift; return o; } enum RepMode { REPNE, // Prefix 0xf2 REPE, // Prefix 0xf3 }; class EmulatorPimpl { public: EmulatorPimpl(RegisterFile *registers, SimCPU *sim_cpu); size_t step(); size_t step_with_io(std::function<void(unsigned long)> io_callback, unsigned long cur_cycle_count); void set_memory(Memory *mem) { this->mem = mem; } void set_io(std::map<uint16_t, IOPorts *> *io) { this->io = io; } bool has_trapped() const { auto int_cs = mem->read<uint16_t>(VEC_INT + 2); auto int_ip = mem->read<uint16_t>(VEC_INT + 0); return registers->get(CS) == int_cs && registers->get(IP) == int_ip; } void reset(); void raise_nmi(); void raise_irq(int irq_num); private: size_t emulate_insn(); void mov88(); void mov89(); void mov8a(); void mov8b(); void movc6(); void movc7(); void movb0_b7(); void movb8_bf(); void mova0(); void mova1(); void mova2(); void mova3(); void mov8e(); void mov8c(); void pusha60(); void popa61(); void pushff(); void push50_57(); void pushsr(); void push68(); void push6a(); void pop8f(); void pop58_5f(); void popsr(); void xchg86(); void xchg87(); void xchg90_97(); void ine4(); void ine5(); void inec(); void ined(); void oute6(); void oute7(); void outee(); void outef(); void setalcd6(); void xlatd7(); void lea8d(); void ldsc5(); void lesc4(); void lahf9f(); void sahf9e(); void pushf9c(); void popf9d(); void add_adc_sub_sbb_cmp_xor_or_and_80(); void add_adc_sub_sbb_cmp_xor_or_and_81(); void add_adc_sub_sbb_cmp_82(); void add_adc_sub_sbb_cmp_83(); void add00(); void add01(); void add02(); void add03(); void add04(); void add05(); void and20(); void and21(); void and22(); void and23(); void and24(); void and25(); void xor30(); void xor31(); void xor32(); void xor33(); void xor34(); void xor35(); void or08(); void or09(); void or0a(); void or0b(); void or0c(); void or0d(); void adc10(); void adc11(); void adc12(); void adc13(); void adc14(); void adc15(); void sub28(); void sub29(); void sub2a(); void sub2b(); void sub2c(); void sub2d(); void sbb18(); void sbb19(); void sbb1a(); void sbb1b(); void sbb1c(); void sbb1d(); void inc_dec_fe(); void incff(); void inc40_47(); void decff(); void dec48_4f(); void aaa37(); void daa27(); void aas3f(); void das2f(); void aamd4(); void negf6(); void negf7(); void notf6(); void notf7(); void cmp38(); void cmp39(); void cmp3a(); void cmp3b(); void cmp3c(); void cmp3d(); void mulf6(); void mulf7(); void imul6b(); void imul69(); void imulf6(); void imulf7(); void divf6(); void divf7(); void idivf6(); void idivf7(); void intcc(); void intcd(); void intoce(); void cbw98(); void cwd99(); void jmpe9(); void jmpeb(); void jmpff_intra(); void jmpff_inter(); void jmpea(); void je74(); void jl7c(); void jle7e(); void jp7a(); void jb72(); void jbe76(); void jo70(); void js78(); void jns79(); void jne75(); void jnl7d(); void jnle7f(); void jnb73(); void jnbe77(); void jnp7b(); void jno71(); void jcxze3(); void calle8(); void callff_intra(); void callff_inter(); void call9a(); void retc3(); void retc2(); void retcb(); void retca(); void iretcf(); void clcf8(); void cmcf5(); void stcf9(); void cldfc(); void stdfd(); void clifa(); void stifb(); void loope2(); void loopee1(); void loopnze0(); void scasbae(); void scasbaf(); void movsba4(); void movswa5(); void cmpsba6(); void cmpswa7(); void lodsbac(); void lodswad(); void stosbaa(); void stoswab(); void hltf4(); void wait9b(); void escd8(); void aadd5(); void shiftc0(); void shiftc1(); void shiftd0(); void shiftd1(); void shiftd2(); void shiftd3(); void shlc0(); void shlc1(); void shld0(); void shld1(); void shld2(); void shld3(); void shrc0(); void shrc1(); void shrd0(); void shrd1(); void shrd2(); void shrd3(); void sarc0(); void sarc1(); void sard0(); void sard1(); void sard2(); void sard3(); void rolc0(); void rolc1(); void rold0(); void rold1(); void rold2(); void rold3(); void rclc0(); void rclc1(); void rcld0(); void rcld1(); void rcld2(); void rcld3(); void rcrc0(); void rcrc1(); void rcrd0(); void rcrd1(); void rcrd2(); void rcrd3(); void rorc0(); void rorc1(); void rord0(); void rord1(); void rord2(); void rord3(); void test84(); void test85(); void testa8(); void testa9(); void testf6(); void testf7(); void bound62(); void outsb6e(); void outsw6f(); void insb6c(); void insw6d(); void leavec9(); void enterc8(); void invalid_opcode(); uint8_t fetch_byte(); template <typename T> std::pair<uint16_t, T> do_mul(int32_t v1, int32_t v2); template <typename T> std::pair<uint16_t, T> do_add(uint16_t v1, uint16_t v2, uint16_t carry_in = 0); template <typename T> std::pair<uint16_t, T> do_sub(uint16_t v1, uint16_t v2, uint16_t carry_in = 0); template <typename T> std::pair<uint16_t, T> do_xor(uint16_t v1, uint16_t v2); template <typename T> std::pair<uint16_t, T> do_or(uint16_t v1, uint16_t v2); template <typename T> std::pair<uint16_t, T> do_and(uint16_t v1, uint16_t v2); void push_inc_jmp_call_ff(); void neg_mul_not_test_div_f6(); void neg_mul_not_test_div_f7(); void push_word(uint16_t v); uint16_t pop_word(); template <typename T> std::pair<uint16_t, T> do_alu( int32_t v1, int32_t v2, int32_t carry_in, std::function<uint32_t(uint32_t, uint32_t, uint32_t)> alu_op); template <typename T> void write_data(T val, bool stack = false); template <typename T> T read_data(bool stack = false); uint16_t fetch_16bit(); GPR get_segment(bool is_stack_reference); void set_override_segment(GPR segment); void write_io8(uint32_t addr, uint8_t val) { if (!io->count(addr & ~1)) return; auto p = (*io)[addr & ~1]; p->write8((addr & ~1) - p->get_base(), addr & 1, val); } void write_io16(uint32_t addr, uint16_t val) { if (!io->count(addr & ~1)) return; auto p = (*io)[addr & ~1]; p->write16((addr & ~1) - p->get_base(), val); } uint8_t read_io8(uint32_t addr) { if (!io->count(addr & ~1)) return 0; auto p = (*io)[addr & ~1]; return p->read8((addr & ~1) - p->get_base(), addr & 1); } uint16_t read_io16(uint32_t addr) { if (!io->count(addr & ~1)) return 0; auto p = (*io)[addr & ~1]; return p->read16((addr & ~1) - p->get_base()); } void do_rep(std::function<void()> primitive, std::function<bool()> should_terminate); bool string_rep_complete(); void handle_nmi(); void handle_irq(); void single_step(); Memory *mem; std::map<uint16_t, IOPorts *> *io; RegisterFile *registers; size_t instr_length = 0; std::unique_ptr<ModRMDecoder> modrm_decoder; uint8_t opcode; bool jump_taken; bool default_segment_overriden; GPR override_segment; RepMode rep_mode; bool has_rep_prefix; bool nmi_pending; bool ext_int_inhibit; uint8_t pending_irq; bool tf_was_set; SimCPU *sim_cpu; }; void EmulatorPimpl::do_rep(std::function<void()> primitive, std::function<bool()> should_terminate) { if (!has_rep_prefix) { primitive(); return; } while (registers->get(CX) != 0) { primitive(); registers->set(CX, registers->get(CX) - 1); if (should_terminate()) break; } } bool EmulatorPimpl::string_rep_complete() { if (rep_mode == REPE && !registers->get_flag(ZF)) return true; if (rep_mode == REPNE && registers->get_flag(ZF)) return true; return false; } EmulatorPimpl::EmulatorPimpl(RegisterFile *registers, SimCPU *sim_cpu) : mem(NULL), io(NULL), registers(registers), jump_taken(false), default_segment_overriden(false), rep_mode(REPE), has_rep_prefix(false), tf_was_set(false), sim_cpu(sim_cpu) { modrm_decoder = std::make_unique<ModRMDecoder>( [&] { return this->fetch_byte(); }, this->registers); reset(); } void EmulatorPimpl::reset() { registers->reset(); opcode = 0; instr_length = 0; nmi_pending = false; ext_int_inhibit = false; pending_irq = 0; } void EmulatorPimpl::raise_nmi() { nmi_pending = true; } void EmulatorPimpl::raise_irq(int irq_num) { pending_irq = irq_num; } void EmulatorPimpl::handle_nmi() { nmi_pending = false; auto flags = registers->get_flags(); push_word(flags); push_word(registers->get(CS)); push_word(registers->get(IP)); flags &= ~(IF | TF); registers->set_flags(flags, IF | TF); auto new_cs = mem->read<uint16_t>(VEC_NMI + 2); auto new_ip = mem->read<uint16_t>(VEC_NMI + 0); registers->set(CS, new_cs); registers->set(IP, new_ip); jump_taken = true; single_step(); } void EmulatorPimpl::handle_irq() { assert(pending_irq); auto irq_num = pending_irq; pending_irq = 0; sim_cpu->ack_int(irq_num); auto flags = registers->get_flags(); push_word(flags); push_word(registers->get(CS)); push_word(registers->get(IP)); flags &= ~(IF | TF); registers->set_flags(flags, IF | TF); auto new_cs = mem->read<uint16_t>(irq_num * 4 + 2); auto new_ip = mem->read<uint16_t>(irq_num * 4 + 0); registers->set(CS, new_cs); registers->set(IP, new_ip); jump_taken = true; single_step(); } void EmulatorPimpl::single_step() { if (!tf_was_set || ext_int_inhibit) return; auto flags = registers->get_flags(); push_word(flags); push_word(registers->get(CS)); push_word(registers->get(IP)); flags &= ~(IF | TF); registers->set_flags(flags, IF | TF); auto new_cs = mem->read<uint16_t>(VEC_SINGLE_STEP + 2); auto new_ip = mem->read<uint16_t>(VEC_SINGLE_STEP + 0); registers->set(CS, new_cs); registers->set(IP, new_ip); jump_taken = true; } size_t EmulatorPimpl::step() { tf_was_set = registers->get_flag(TF); if (nmi_pending && !ext_int_inhibit) { handle_nmi(); return 0; } else if (pending_irq && !ext_int_inhibit && registers->get_flag(IF)) { handle_irq(); return 0; } auto len = emulate_insn(); // Hardware also processes interrupt entry at the tail of an instruction // rather than a conditional check at the start of each instruction. So // when single stepping, stepping an instruction may retire that // instruction and then jump to the NMI/INT handler. if (nmi_pending && !ext_int_inhibit) handle_nmi(); else if (pending_irq && !ext_int_inhibit && registers->get_flag(IF)) handle_irq(); else if (!ext_int_inhibit) single_step(); return len; } size_t EmulatorPimpl::step_with_io( std::function<void(unsigned long)> io_callback, unsigned long cur_cycle_count) { io_callback(cur_cycle_count); return this->step(); } size_t EmulatorPimpl::emulate_insn() { instr_length = 0; bool processing_prefixes; default_segment_overriden = false; has_rep_prefix = false; modrm_decoder->clear(); jump_taken = false; ext_int_inhibit = false; do { processing_prefixes = false; opcode = fetch_byte(); // clang-format off switch (opcode) { case 0x06: // fallthrough case 0x0e: // fallthrough case 0x16: // fallthrough case 0x1e: pushsr(); break; case 0x0f: invalid_opcode(); break; case 0x07: // fallthrough case 0x17: // fallthrough case 0x1f: popsr(); break; case 0x00: add00(); break; case 0x01: add01(); break; case 0x02: add02(); break; case 0x03: add03(); break; case 0x04: add04(); break; case 0x05: add05(); break; case 0x08: or08(); break; case 0x09: or09(); break; case 0x0a: or0a(); break; case 0x0b: or0b(); break; case 0x0c: or0c(); break; case 0x0d: or0d(); break; case 0x10: adc10(); break; case 0x11: adc11(); break; case 0x12: adc12(); break; case 0x13: adc13(); break; case 0x14: adc14(); break; case 0x15: adc15(); break; case 0x18: sbb18(); break; case 0x19: sbb19(); break; case 0x1a: sbb1a(); break; case 0x1b: sbb1b(); break; case 0x1c: sbb1c(); break; case 0x1d: sbb1d(); break; case 0x20: and20(); break; case 0x21: and21(); break; case 0x22: and22(); break; case 0x23: and23(); break; case 0x24: and24(); break; case 0x25: and25(); break; case 0x27: daa27(); break; case 0x28: sub28(); break; case 0x29: sub29(); break; case 0x2a: sub2a(); break; case 0x2b: sub2b(); break; case 0x2c: sub2c(); break; case 0x2d: sub2d(); break; case 0x2f: das2f(); break; case 0x30: xor30(); break; case 0x31: xor31(); break; case 0x32: xor32(); break; case 0x33: xor33(); break; case 0x34: xor34(); break; case 0x35: xor35(); break; case 0x37: aaa37(); break; case 0x38: cmp38(); break; case 0x39: cmp39(); break; case 0x3a: cmp3a(); break; case 0x3b: cmp3b(); break; case 0x3c: cmp3c(); break; case 0x3d: cmp3d(); break; case 0x3f: aas3f(); break; case 0x40 ... 0x47: inc40_47(); break; case 0x48 ... 0x4f: dec48_4f(); break; case 0x50 ... 0x57: push50_57(); break; case 0x58 ... 0x5f: pop58_5f(); break; case 0x60: pusha60(); break; case 0x61: popa61(); break; case 0x62: bound62(); break; case 0x63: invalid_opcode(); break; case 0x64: invalid_opcode(); break; case 0x65: invalid_opcode(); break; case 0x66: invalid_opcode(); break; case 0x67: invalid_opcode(); break; case 0x68: push68(); break; case 0x69: imul69(); break; case 0x6a: push6a(); break; case 0x6b: imul6b(); break; case 0x6c: insb6c(); break; case 0x6d: insw6d(); break; case 0x6e: outsb6e(); break; case 0x6f: outsw6f(); break; case 0x70: jo70(); break; case 0x71: jno71(); break; case 0x72: jb72(); break; case 0x73: jnb73(); break; case 0x74: je74(); break; case 0x75: jne75(); break; case 0x76: jbe76(); break; case 0x77: jnbe77(); break; case 0x78: js78(); break; case 0x79: jns79(); break; case 0x7a: jp7a(); break; case 0x7b: jnp7b(); break; case 0x7c: jl7c(); break; case 0x7d: jnl7d(); break; case 0x7e: jle7e(); break; case 0x7f: jnle7f(); break; case 0x80: add_adc_sub_sbb_cmp_xor_or_and_80(); break; case 0x81: add_adc_sub_sbb_cmp_xor_or_and_81(); break; case 0x82: add_adc_sub_sbb_cmp_82(); break; case 0x83: add_adc_sub_sbb_cmp_83(); break; case 0x84: test84(); break; case 0x85: test85(); break; case 0x86: xchg86(); break; case 0x87: xchg87(); break; case 0x88: mov88(); break; case 0x89: mov89(); break; case 0x8a: mov8a(); break; case 0x8b: mov8b(); break; case 0x8c: mov8c(); break; case 0x8d: lea8d(); break; case 0x8e: mov8e(); break; case 0x8f: pop8f(); break; case 0x90 ... 0x97: xchg90_97(); break; case 0x98: cbw98(); break; case 0x99: cwd99(); break; case 0x9a: call9a(); break; case 0x9b: wait9b(); break; case 0x9c: pushf9c(); break; case 0x9d: popf9d(); break; case 0x9e: sahf9e(); break; case 0x9f: lahf9f(); break; case 0xa0: mova0(); break; case 0xa1: mova1(); break; case 0xa2: mova2(); break; case 0xa3: mova3(); break; case 0xa4: movsba4(); break; case 0xa5: movswa5(); break; case 0xa6: cmpsba6(); break; case 0xa7: cmpswa7(); break; case 0xa8: testa8(); break; case 0xa9: testa9(); break; case 0xaa: stosbaa(); break; case 0xab: stoswab(); break; case 0xac: lodsbac(); break; case 0xad: lodswad(); break; case 0xae: scasbae(); break; case 0xaf: scasbaf(); break; case 0xb0 ... 0xb7: movb0_b7(); break; case 0xb8 ... 0xbf: movb8_bf(); break; case 0xc0: shiftc0(); break; case 0xc1: shiftc1(); break; case 0xc2: retc2(); break; case 0xc3: retc3(); break; case 0xc4: lesc4(); break; case 0xc5: ldsc5(); break; case 0xc6: movc6(); break; case 0xc7: movc7(); break; case 0xc8: enterc8(); break; case 0xc9: leavec9(); break; case 0xca: retca(); break; case 0xcb: retcb(); break; case 0xcc: intcc(); break; case 0xcd: intcd(); break; case 0xce: intoce(); break; case 0xcf: iretcf(); break; case 0xd0: shiftd0(); break; case 0xd1: shiftd1(); break; case 0xd2: shiftd2(); break; case 0xd3: shiftd3(); break; case 0xd4: aamd4(); break; case 0xd5: aadd5(); break; case 0xd6: setalcd6(); break; case 0xd7: xlatd7(); break; case 0xd8 ... 0xdf: escd8(); break; case 0xe0: loopnze0(); break; case 0xe1: loopee1(); break; case 0xe2: loope2(); break; case 0xe3: jcxze3(); break; case 0xe4: ine4(); break; case 0xe5: ine5(); break; case 0xe6: oute6(); break; case 0xe7: oute7(); break; case 0xe8: calle8(); break; case 0xe9: jmpe9(); break; case 0xea: jmpea(); break; case 0xeb: jmpeb(); break; case 0xec: inec(); break; case 0xed: ined(); break; case 0xee: outee(); break; case 0xef: outef(); break; case 0xf1: invalid_opcode(); break; case 0xf4: hltf4(); break; case 0xf5: cmcf5(); break; case 0xf6: neg_mul_not_test_div_f6(); break; case 0xf7: neg_mul_not_test_div_f7(); break; case 0xf8: clcf8(); break; case 0xf9: stcf9(); break; case 0xfa: clifa(); break; case 0xfb: stifb(); break; case 0xfc: cldfc(); break; case 0xfd: stdfd(); break; case 0xfe: inc_dec_fe(); break; case 0xff: push_inc_jmp_call_ff(); break; case 0xf0: // lock processing_prefixes = true; break; case 0x26: set_override_segment(ES); processing_prefixes = true; break; case 0x2e: set_override_segment(CS); processing_prefixes = true; break; case 0x36: set_override_segment(SS); processing_prefixes = true; break; case 0x3e: set_override_segment(DS); processing_prefixes = true; break; case 0xf2: rep_mode = REPNE; has_rep_prefix = true; processing_prefixes = true; break; case 0xf3: rep_mode = REPE; has_rep_prefix = true; processing_prefixes = true; break; default: std::cerr << "warning: unknown opcode 0x" << std::hex << (unsigned)opcode << " at " << (unsigned)registers->get(CS) << ":" << (unsigned)registers->get(IP) << std::endl; } // clang-format on } while (processing_prefixes); if (!jump_taken) registers->set(IP, registers->get(IP) + instr_length); return instr_length; } void EmulatorPimpl::push_inc_jmp_call_ff() { modrm_decoder->set_width(OP_WIDTH_16); modrm_decoder->decode(); if (modrm_decoder->raw_reg() == 6) pushff(); else if (modrm_decoder->raw_reg() == 0) incff(); else if (modrm_decoder->raw_reg() == 1) decff(); else if (modrm_decoder->raw_reg() == 2) callff_intra(); else if (modrm_decoder->raw_reg() == 3) callff_inter(); else if (modrm_decoder->raw_reg() == 4) jmpff_intra(); else if (modrm_decoder->raw_reg() == 5) jmpff_inter(); else invalid_opcode(); } template <typename T> std::pair<uint16_t, T> EmulatorPimpl::do_alu( int32_t v1, int32_t v2, int32_t carry, std::function<uint32_t(uint32_t, uint32_t, uint32_t)> alu_op) { uint16_t flags = registers->get_flags(); flags &= ~(AF | CF | OF | PF | SF | ZF); uint32_t result32 = alu_op(static_cast<uint32_t>(v1), static_cast<uint32_t>(v2), static_cast<uint32_t>(carry)); bool af = !!(alu_op(v1 & 0xf, v2 & 0xf, carry) & (1 << 4)); auto sign_bit = (8 * sizeof(T)) - 1; auto carry_bit = (8 * sizeof(T)); if (af) flags |= AF; if (result32 & (1 << carry_bit)) flags |= CF; if (result32 & (1 << sign_bit)) flags |= SF; if ((result32 & static_cast<T>(-1)) == 0) flags |= ZF; if (!__builtin_parity(result32 & static_cast<uint8_t>(-1))) flags |= PF; bool carry_in = !!(alu_op(static_cast<uint32_t>(v1) & ~(1 << sign_bit), static_cast<uint32_t>(v2) & ~(1 << sign_bit), static_cast<uint32_t>(carry)) & (1 << sign_bit)); if (carry_in ^ !!(flags & CF)) flags |= OF; return std::make_pair(flags, static_cast<T>(result32)); } template <typename T> std::pair<uint16_t, T> EmulatorPimpl::do_add(uint16_t v1, uint16_t v2, uint16_t carry_in) { return do_alu<T>(v1, v2, carry_in, [](uint32_t a, uint32_t b, uint32_t c) -> uint32_t { return a + b + c; }); } template <typename T> std::pair<uint16_t, T> EmulatorPimpl::do_xor(uint16_t v1, uint16_t v2) { return do_alu<T>( v1, v2, 0, [](uint32_t a, uint32_t b, uint32_t __attribute__((unused)) c) -> uint32_t { return a ^ b; }); } template <typename T> std::pair<uint16_t, T> EmulatorPimpl::do_or(uint16_t v1, uint16_t v2) { return do_alu<T>( v1, v2, 0, [](uint32_t a, uint32_t b, uint32_t __attribute__((unused)) c) -> uint32_t { return a | b; }); } template <typename T> std::pair<uint16_t, T> EmulatorPimpl::do_and(uint16_t v1, uint16_t v2) { return do_alu<T>( v1, v2, 0, [](uint32_t a, uint32_t b, uint32_t __attribute__((unused)) c) -> uint32_t { return a & b; }); } template <typename T> std::pair<uint16_t, T> EmulatorPimpl::do_sub(uint16_t v1, uint16_t v2, uint16_t carry_in) { return do_alu<T>(v1, v2, carry_in, [](uint32_t a, uint32_t b, uint32_t c) -> uint32_t { return a - b - c; }); } template <typename T> std::pair<uint16_t, T> EmulatorPimpl::do_mul(int32_t v1, int32_t v2) { return do_alu<T>( v1, v2, 0, [](uint32_t a, uint32_t b, uint32_t __attribute__((unused)) c) -> uint32_t { return a * b; }); } void EmulatorPimpl::add_adc_sub_sbb_cmp_xor_or_and_80() { modrm_decoder->set_width(OP_WIDTH_8); modrm_decoder->decode(); uint8_t v1 = read_data<uint8_t>(); uint8_t v2 = fetch_byte(); bool carry_in = modrm_decoder->raw_reg() == 2 || modrm_decoder->raw_reg() == 3 ? !!(registers->get_flags() & CF) : 0; uint8_t result; uint16_t flags; uint16_t update_mask = OF | SF | ZF | CF | PF | AF; if (modrm_decoder->raw_reg() == 0 || modrm_decoder->raw_reg() == 2) std::tie(flags, result) = do_add<uint8_t>(v1, v2, carry_in); else if (modrm_decoder->raw_reg() == 6) { std::tie(flags, result) = do_xor<uint8_t>(v1, v2); flags &= ~(CF | OF); update_mask &= ~AF; } else if (modrm_decoder->raw_reg() == 1) { std::tie(flags, result) = do_or<uint8_t>(v1, v2); flags &= ~(CF | OF); update_mask &= ~AF; } else if (modrm_decoder->raw_reg() == 4) { std::tie(flags, result) = do_and<uint8_t>(v1, v2); flags &= ~(CF | OF); update_mask &= ~AF; } else std::tie(flags, result) = do_sub<uint8_t>(v1, v2, carry_in); registers->set_flags(flags, update_mask); // cmp doesn't write the result if (modrm_decoder->raw_reg() != 7) write_data<uint8_t>(result & 0xff); } void EmulatorPimpl::add_adc_sub_sbb_cmp_82() { add_adc_sub_sbb_cmp_xor_or_and_80(); } // add r/m, immediate, 16-bit void EmulatorPimpl::add_adc_sub_sbb_cmp_xor_or_and_81() { modrm_decoder->set_width(OP_WIDTH_16); modrm_decoder->decode(); uint16_t v1 = read_data<uint16_t>(); uint16_t v2 = fetch_16bit(); bool carry_in = modrm_decoder->raw_reg() == 2 || modrm_decoder->raw_reg() == 3 ? !!(registers->get_flags() & CF) : 0; uint16_t result; uint16_t flags; uint16_t update_mask = OF | SF | ZF | CF | PF | AF; if (modrm_decoder->raw_reg() == 0 || modrm_decoder->raw_reg() == 2) std::tie(flags, result) = do_add<uint16_t>(v1, v2, carry_in); else if (modrm_decoder->raw_reg() == 6) { std::tie(flags, result) = do_xor<uint16_t>(v1, v2); flags &= ~(CF | OF); update_mask &= ~AF; } else if (modrm_decoder->raw_reg() == 1) { std::tie(flags, result) = do_or<uint16_t>(v1, v2); flags &= ~(CF | OF); update_mask &= ~AF; } else if (modrm_decoder->raw_reg() == 4) { std::tie(flags, result) = do_and<uint16_t>(v1, v2); flags &= ~(CF | OF); update_mask &= ~AF; } else std::tie(flags, result) = do_sub<uint16_t>(v1, v2, carry_in); registers->set_flags(flags, update_mask); // cmp doesn't write the result if (modrm_decoder->raw_reg() != 7) write_data<uint16_t>(result & 0xffff); } // add r/m, immediate, 8-bit, sign-extended void EmulatorPimpl::add_adc_sub_sbb_cmp_83() { modrm_decoder->set_width(OP_WIDTH_16); modrm_decoder->decode(); uint16_t v1 = read_data<uint16_t>(); int16_t v2 = sign_extend<int16_t, uint8_t>(fetch_byte()); bool carry_in = modrm_decoder->raw_reg() == 2 || modrm_decoder->raw_reg() == 3 ? !!(registers->get_flags() & CF) : 0; uint16_t result; uint16_t flags; uint16_t update_mask = OF | SF | ZF | CF | PF | AF; if (modrm_decoder->raw_reg() == 0 || modrm_decoder->raw_reg() == 2) std::tie(flags, result) = do_add<uint16_t>(v1, v2, carry_in); else if (modrm_decoder->raw_reg() == 6) { std::tie(flags, result) = do_xor<uint16_t>(v1, v2); flags &= ~(CF | OF); update_mask &= ~AF; } else if (modrm_decoder->raw_reg() == 1) { std::tie(flags, result) = do_or<uint16_t>(v1, v2); flags &= ~(CF | OF); update_mask &= ~AF; } else if (modrm_decoder->raw_reg() == 4) { std::tie(flags, result) = do_and<uint16_t>(v1, v2); flags &= ~(CF | OF); update_mask &= ~AF; } else std::tie(flags, result) = do_sub<uint16_t>(v1, v2, carry_in); registers->set_flags(flags, update_mask); // cmp doesn't write the result if (modrm_decoder->raw_reg() != 7) write_data<uint16_t>(result & 0xffff); } uint8_t EmulatorPimpl::fetch_byte() { return mem->read<uint8_t>( get_phys_addr(registers->get(CS), registers->get(IP) + instr_length++)); } void EmulatorPimpl::neg_mul_not_test_div_f6() { modrm_decoder->set_width(OP_WIDTH_8); modrm_decoder->decode(); if (modrm_decoder->raw_reg() == 0x2) notf6(); else if (modrm_decoder->raw_reg() == 0x3) negf6(); else if (modrm_decoder->raw_reg() == 0x4) mulf6(); else if (modrm_decoder->raw_reg() == 0x5) imulf6(); else if (modrm_decoder->raw_reg() == 0x0 || modrm_decoder->raw_reg() == 1) testf6(); else if (modrm_decoder->raw_reg() == 0x6) divf6(); else if (modrm_decoder->raw_reg() == 0x7) idivf6(); else std::cerr << "warning: invalid reg " << std::hex << (unsigned)modrm_decoder->raw_reg() << " for opcode 0x" << (unsigned)opcode << std::endl; } void EmulatorPimpl::neg_mul_not_test_div_f7() { modrm_decoder->set_width(OP_WIDTH_16); modrm_decoder->decode(); if (modrm_decoder->raw_reg() == 0x2) notf7(); else if (modrm_decoder->raw_reg() == 0x3) negf7(); else if (modrm_decoder->raw_reg() == 0x4) mulf7(); else if (modrm_decoder->raw_reg() == 0x5) imulf7(); else if (modrm_decoder->raw_reg() == 0x0 || modrm_decoder->raw_reg() == 1) testf7(); else if (modrm_decoder->raw_reg() == 0x6) divf7(); else if (modrm_decoder->raw_reg() == 0x7) idivf7(); else std::cerr << "warning: invalid reg " << std::hex << (unsigned)modrm_decoder->raw_reg() << " for opcode 0x" << (unsigned)opcode << std::endl; } void EmulatorPimpl::shiftc0() { modrm_decoder->set_width(OP_WIDTH_8); modrm_decoder->decode(); if (modrm_decoder->raw_reg() == 4 || modrm_decoder->raw_reg() == 6) shlc0(); else if (modrm_decoder->raw_reg() == 5) shrc0(); else if (modrm_decoder->raw_reg() == 7) sarc0(); else if (modrm_decoder->raw_reg() == 0) rolc0(); else if (modrm_decoder->raw_reg() == 2) rclc0(); else if (modrm_decoder->raw_reg() == 3) rcrc0(); else if (modrm_decoder->raw_reg() == 1) rorc0(); else std::cerr << "warning: invalid reg " << std::hex << (unsigned)modrm_decoder->raw_reg() << " for opcode 0x" << (unsigned)opcode << std::endl; } void EmulatorPimpl::shiftc1() { modrm_decoder->set_width(OP_WIDTH_16); modrm_decoder->decode(); if (modrm_decoder->raw_reg() == 4 || modrm_decoder->raw_reg() == 6) shlc1(); else if (modrm_decoder->raw_reg() == 5) shrc1(); else if (modrm_decoder->raw_reg() == 7) sarc1(); else if (modrm_decoder->raw_reg() == 0) rolc1(); else if (modrm_decoder->raw_reg() == 2) rclc1(); else if (modrm_decoder->raw_reg() == 3) rcrc1(); else if (modrm_decoder->raw_reg() == 1) rorc1(); else std::cerr << "warning: invalid reg " << std::hex << (unsigned)modrm_decoder->raw_reg() << " for opcode 0x" << (unsigned)opcode << std::endl; } void EmulatorPimpl::shiftd0() { modrm_decoder->set_width(OP_WIDTH_8); modrm_decoder->decode(); if (modrm_decoder->raw_reg() == 4 || modrm_decoder->raw_reg() == 6) shld0(); else if (modrm_decoder->raw_reg() == 5) shrd0(); else if (modrm_decoder->raw_reg() == 7) sard0(); else if (modrm_decoder->raw_reg() == 0) rold0(); else if (modrm_decoder->raw_reg() == 2) rcld0(); else if (modrm_decoder->raw_reg() == 3) rcrd0(); else if (modrm_decoder->raw_reg() == 1) rord0(); else std::cerr << "warning: invalid reg " << std::hex << (unsigned)modrm_decoder->raw_reg() << " for opcode 0x" << (unsigned)opcode << std::endl; } void EmulatorPimpl::shiftd1() { modrm_decoder->set_width(OP_WIDTH_16); modrm_decoder->decode(); if (modrm_decoder->raw_reg() == 4 || modrm_decoder->raw_reg() == 6) shld1(); else if (modrm_decoder->raw_reg() == 5) shrd1(); else if (modrm_decoder->raw_reg() == 7) sard1(); else if (modrm_decoder->raw_reg() == 0) rold1(); else if (modrm_decoder->raw_reg() == 2) rcld1(); else if (modrm_decoder->raw_reg() == 3) rcrd1(); else if (modrm_decoder->raw_reg() == 1) rord1(); else std::cerr << "warning: invalid reg " << std::hex << (unsigned)modrm_decoder->raw_reg() << " for opcode 0x" << (unsigned)opcode << std::endl; } void EmulatorPimpl::shiftd2() { modrm_decoder->set_width(OP_WIDTH_8); modrm_decoder->decode(); if (modrm_decoder->raw_reg() == 4 || modrm_decoder->raw_reg() == 6) shld2(); else if (modrm_decoder->raw_reg() == 5) shrd2(); else if (modrm_decoder->raw_reg() == 7) sard2(); else if (modrm_decoder->raw_reg() == 0) rold2(); else if (modrm_decoder->raw_reg() == 2) rcld2(); else if (modrm_decoder->raw_reg() == 1) rord2(); else if (modrm_decoder->raw_reg() == 3) rcrd2(); else std::cerr << "warning: invalid reg " << std::hex << (unsigned)modrm_decoder->raw_reg() << " for opcode 0x" << (unsigned)opcode << std::endl; } void EmulatorPimpl::shiftd3() { modrm_decoder->set_width(OP_WIDTH_16); modrm_decoder->decode(); if (modrm_decoder->raw_reg() == 4 || modrm_decoder->raw_reg() == 6) shld3(); else if (modrm_decoder->raw_reg() == 5) shrd3(); else if (modrm_decoder->raw_reg() == 7) sard3(); else if (modrm_decoder->raw_reg() == 0) rold3(); else if (modrm_decoder->raw_reg() == 2) rcld3(); else if (modrm_decoder->raw_reg() == 1) rord3(); else if (modrm_decoder->raw_reg() == 3) rcrd3(); else std::cerr << "warning: invalid reg " << std::hex << (unsigned)modrm_decoder->raw_reg() << " for opcode 0x" << (unsigned)opcode << std::endl; } void EmulatorPimpl::invalid_opcode() { auto flags = registers->get_flags(); push_word(flags); push_word(registers->get(CS)); push_word(registers->get(IP) + instr_length); flags &= ~(IF | TF); registers->set_flags(flags, IF | TF); // int 3 auto new_cs = mem->read<uint16_t>(VEC_INVALID_OPCODE + 2); auto new_ip = mem->read<uint16_t>(VEC_INVALID_OPCODE + 0); registers->set(CS, new_cs); registers->set(IP, new_ip); jump_taken = true; } #include "instructions/mov.cpp" #include "instructions/push.cpp" #include "instructions/pop.cpp" #include "instructions/xchg.cpp" #include "instructions/io.cpp" #include "instructions/xlat.cpp" #include "instructions/lea.cpp" #include "instructions/lahf_sahf.cpp" #include "instructions/add.cpp" #include "instructions/xor.cpp" #include "instructions/and.cpp" #include "instructions/or.cpp" #include "instructions/adc.cpp" #include "instructions/sub.cpp" #include "instructions/sbb.cpp" #include "instructions/inc_dec.cpp" #include "instructions/aaa.cpp" #include "instructions/daa.cpp" #include "instructions/aas.cpp" #include "instructions/aam.cpp" #include "instructions/das.cpp" #include "instructions/aad.cpp" #include "instructions/neg.cpp" #include "instructions/cmp.cpp" #include "instructions/mul.cpp" #include "instructions/imul.cpp" #include "instructions/div.cpp" #include "instructions/int.cpp" #include "instructions/cbw.cpp" #include "instructions/cwd.cpp" #include "instructions/jmp.cpp" #include "instructions/call.cpp" #include "instructions/ret.cpp" #include "instructions/clc.cpp" #include "instructions/cmc.cpp" #include "instructions/stc.cpp" #include "instructions/setalc.cpp" #include "instructions/cld.cpp" #include "instructions/std.cpp" #include "instructions/cli.cpp" #include "instructions/sti.cpp" #include "instructions/loop.cpp" #include "instructions/loope.cpp" #include "instructions/loopnz.cpp" #include "instructions/scas.cpp" #include "instructions/movs.cpp" #include "instructions/cmps.cpp" #include "instructions/lods.cpp" #include "instructions/stos.cpp" #include "instructions/hlt.cpp" #include "instructions/wait.cpp" #include "instructions/esc.cpp" #include "instructions/not.cpp" #include "instructions/shl.cpp" #include "instructions/shr.cpp" #include "instructions/sar.cpp" #include "instructions/rol.cpp" #include "instructions/rcl.cpp" #include "instructions/ror.cpp" #include "instructions/rcr.cpp" #include "instructions/test.cpp" #include "instructions/bound.cpp" #include "instructions/outs.cpp" #include "instructions/ins.cpp" #include "instructions/leave.cpp" #include "instructions/enter.cpp" void EmulatorPimpl::push_word(uint16_t v) { registers->set(SP, registers->get(SP) - 2); auto addr = get_phys_addr(registers->get(SS), registers->get(SP)); mem->write<uint16_t>(addr, v); } uint16_t EmulatorPimpl::pop_word() { auto addr = get_phys_addr(registers->get(SS), registers->get(SP)); auto v = mem->read<uint16_t>(addr); registers->set(SP, registers->get(SP) + 2); return v; } uint16_t EmulatorPimpl::fetch_16bit() { uint16_t immed = (static_cast<uint16_t>(fetch_byte()) | (static_cast<uint16_t>(fetch_byte()) << 8)); return immed; } template <typename T> void EmulatorPimpl::write_data(T val, bool stack) { if (modrm_decoder->rm_type() == OP_REG) { auto dest = modrm_decoder->rm_reg(); registers->set(dest, val); } else { auto ea = modrm_decoder->effective_address(); auto segment = get_segment(stack); auto addr = get_phys_addr(registers->get(segment), ea); mem->write<T>(addr, val); } } template <typename T> T EmulatorPimpl::read_data(bool stack) { if (modrm_decoder->rm_type() == OP_MEM) { auto displacement = modrm_decoder->effective_address(); auto segment = get_segment(stack); auto addr = get_phys_addr(registers->get(segment), displacement); return mem->read<T>(addr); } else { auto source = modrm_decoder->rm_reg(); return registers->get(source); } } GPR EmulatorPimpl::get_segment(bool is_stack_reference) { if (is_stack_reference) return SS; if (default_segment_overriden) return override_segment; return modrm_decoder->uses_bp_as_base() || is_stack_reference ? SS : DS; } void EmulatorPimpl::set_override_segment(GPR segment) { default_segment_overriden = true; override_segment = segment; } Emulator::Emulator(RegisterFile *registers, SoftwareCPU *cpu) : pimpl(std::make_unique<EmulatorPimpl>(registers, cpu)) { } Emulator::~Emulator() { } size_t Emulator::step() { ++num_cycles; return pimpl->step(); } size_t Emulator::step_with_io(std::function<void(unsigned long)> io_callback) { ++num_cycles; return pimpl->step_with_io(io_callback, num_cycles); } void Emulator::set_memory(Memory *mem) { pimpl->set_memory(mem); } void Emulator::set_io(std::map<uint16_t, IOPorts *> *io) { pimpl->set_io(io); } bool Emulator::has_trapped() const { return pimpl->has_trapped(); } void Emulator::reset() { pimpl->reset(); } void Emulator::raise_nmi() { pimpl->raise_nmi(); } void Emulator::raise_irq(int irq_num) { pimpl->raise_irq(irq_num); } unsigned long Emulator::cycle_count() const { return num_cycles; }