Nowadays, there are numerous implementations of x86 emulators. To illustrate how these emulators handle instructions, I have chosen the ADD mnemonic, a fundamental x86 instruction, and examined its implementation across different emulators.
- Qemu
- Bochs
- DosBox-X
- Neko Project II Kai
- NxVM
- Box64
- Valgrind
- libx86emu
- Blink
- Fex
- x86emu (shift-corps)
- Halfix
- Niwaka X86
- v86
- 86Box
- x86emu (cseagle)
- MartyPC
- dax86
- TowerPC
- jPC
- x64emu (sdvassilev)
- ThreeAteSix
- tinyx86
- pce
Qemu
Qemu using TCG/JIT, it does not provide implementation but only translation, for example the x86 add instruction on x86 host: https://github.com/qemu/qemu/tcg/i386/tcg-target.c.inc
case ARITH_ADD:
case ARITH_SUB:
if (!cf) {
/*
* ??? While INC is 2 bytes shorter than ADDL $1, they also induce
* partial flags update stalls on Pentium4 and are not recommended
* by current Intel optimization manuals.
*/
if (val == 1 || val == -1) {
int is_inc = (c == ARITH_ADD) ^ (val < 0);
if (TCG_TARGET_REG_BITS == 64) {
/*
* The single-byte increment encodings are re-tasked
* as the REX prefixes. Use the MODRM encoding.
*/
tcg_out_modrm(s, OPC_GRP5 + rexw,
(is_inc ? EXT5_INC_Ev : EXT5_DEC_Ev), r0);
} else {
tcg_out8(s, (is_inc ? OPC_INC_r32 : OPC_DEC_r32) + r0);
}
return;
}
if (val == 128) {
/*
* Facilitate using an 8-bit immediate. Carry is inverted
* by this transformation, so do it only if cf == 0.
*/
c ^= ARITH_ADD ^ ARITH_SUB;
val = -128;
}
}
break;
if (val == (int8_t)val) {
tcg_out_modrm(s, OPC_ARITH_EvIb + rexw, c, r0);
tcg_out8(s, val);
return;
}
if (rexw == 0 || val == (int32_t)val) {
tcg_out_modrm(s, OPC_ARITH_EvIz + rexw, c, r0);
tcg_out32(s, val);
return;
}
Other projects that utilizing Qemu:
Xemu the Original Xbox Emulator and using Qemu TCG, because Xbox (1st gen) using Intel Pentum III for its CPU.
Unicorn Engine, a CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)
Some instruction is not generated usingt tcg instead using helper, for example FPU operations. https://github.com/unicorn-engine/unicorn/qemu/target/i386/fpu_helper.c
Bochs
Boch is IA-32 Emulator.
Sample of its add instruction: https://github.com/ipxe/bochs/cpu/arith64.cc
BX_CPU_C::ADD_EqGqM(bxInstruction_c *i)
{
Bit64u op1_64, op2_64, sum_64;
bx_address eaddr = BX_CPU_RESOLVE_ADDR_64(i);
/* pointer, segment address pair */
op1_64 = read_RMW_linear_qword(i->seg(), get_laddr64(i->seg(), eaddr));
op2_64 = BX_READ_64BIT_REG(i->src());
sum_64 = op1_64 + op2_64;
write_RMW_linear_qword(sum_64);
SET_FLAGS_OSZAPC_ADD_64(op1_64, op2_64, sum_64);
BX_NEXT_INSTR(i);
}
DosBox-X
DOSBox-X is a DOS-emulator that emulates CPU:286/386 realmode.
Sample of its add instruction: https://github.com/joncampbell123/dosbox-x/src/cpu/instructions.h
#define ADDB(op1,op2,load,save) \
lf_var1b=load(op1);lf_var2b=op2; \
lf_resb=lf_var1b+lf_var2b; \
save(op1,lf_resb); \
lflags.type=t_ADDb;
or: https://github.com/joncampbell123/dosbox-x/src/cpu/core_full/op.h
case t_ADDb:
case t_ADDw:
case t_ADDd:
lf_var1d=inst_op1_d;
lf_var2d=inst_op2_d;
inst_op1_d=lf_resd=lf_var1d + lf_var2d;
lflags.type=inst.code.op;
break;
It has dyanarec/JIT, a sample for add instruction: https://github.com/joncampbell123/dosbox-x/src/cpu/core_dyn_x86/decoder.h
static void dyn_dop_ebgb(DualOps op) { // ex: for add, op == DOP_ADD
dyn_get_modrm();
DynReg * rm_reg=&DynRegs[decode.modrm.reg&3];
if (decode.modrm.mod<3) {
dyn_fill_ea();
if ((op<=DOP_TEST) && (op!=DOP_ADC && op!=DOP_SBB)) set_skipflags(true);
dyn_read_byte(DREG(EA),DREG(TMPB),false);
if (op<=DOP_TEST) {
if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
else set_skipflags(false);
}
gen_dop_byte(op,DREG(TMPB),0,rm_reg,decode.modrm.reg&4);
if (op!=DOP_CMP) dyn_write_byte_release(DREG(EA),DREG(TMPB),false);
else gen_releasereg(DREG(EA));
gen_releasereg(DREG(TMPB));
} else {
if (op<=DOP_TEST) {
if (op==DOP_ADC || op==DOP_SBB) gen_needcarry();
else gen_discardflags();
}
gen_dop_byte(op,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4,rm_reg,decode.modrm.reg&4);
}
}
Neko Project II Kai
Neko Project II Kai is PC-9801 Emulator. PC-98 Using Intel 8086 processor.
Sample of its add instruction: https://github.com/AZO234/NP2kai/i386c/ia32/ia32.mcr
/*
* arith
*/
#define _ADD_BYTE(r, d, s) \
do { \
(r) = (s) + (d); \
CPU_OV = ((r) ^ (s)) & ((r) ^ (d)) & 0x80; \
CPU_FLAGL = (UINT8)(((r) ^ (d) ^ (s)) & A_FLAG); \
CPU_FLAGL |= szpcflag[(r) & 0x1ff]; \
} while (/*CONSTCOND*/ 0)
NxVM
Neko’s x86 Virtual Machine is a portable x86 PC emulator with debugger. Its written in C.
A Sample of add instruction: https://github.com/cshaxu/nxvm/src/device/vcpuins.c
static void _a_add(t_nubit64 cdest, t_nubit64 csrc, t_nubit8 bit) {
_cb("_a_add");
_kac_arith2(ADD_FLAG,
ADD8, (vcpuins.data.opr1 + vcpuins.data.opr2),
ADD16, (vcpuins.data.opr1 + vcpuins.data.opr2),
ADD16, (vcpuins.data.opr1 + vcpuins.data.opr2),
ADD32, (vcpuins.data.opr1 + vcpuins.data.opr2),
ADD32, (vcpuins.data.opr1 + vcpuins.data.opr2));
_ce;
}
Box64
Box64 is Linux Userspace x86_64 Emulator. Its written in C
Sample of its add instruction as emulator: https://github.com/ptitSeb/box64/src/emu/x64primop.h
static inline uint64_t add64(x64emu_t *emu, uint64_t d, uint64_t s)
{
emu->res.u64 = d + s;
emu->op1.u64 = d;
emu->op2.u64 = s;
emu->df = d_add64;
return emu->res.u64;
}
It has dynarec/JIT or dynamic recompilation feature, a sample for its add instruction on arm64 host: https://github.com/ptitSeb/box64/src/dynarec/arm64/dynarec_arm64_00.c
case 0x00:
INST_NAME("ADD Eb, Gb");
SETFLAGS(X_ALL, SF_SET_PENDING);
nextop = F8;
GETEB(x1, 0);
GETGB(x2);
emit_add8(dyn, ninst, x1, x2, x4, x5);
EBBACK;
Valgrind
Valgrind is a GPL’d system for debugging and profiling Linux programs. It has CPU emulator thru VEX IR translation. A sample for its add instruction for host x86 on valgrind.git/VEX/priv/host_x86_isel.c:
/* Is it an addition or logical style op? */
switch (e->Iex.Binop.op) {
case Iop_Add8: case Iop_Add16: case Iop_Add32:
aluOp = Xalu_ADD; break;
...
}
if (aluOp != Xalu_INVALID) {
HReg dst = newVRegI(env);
HReg reg = iselIntExpr_R(env, e->Iex.Binop.arg1);
X86RMI* rmi = iselIntExpr_RMI(env, e->Iex.Binop.arg2);
addInstr(env, mk_iMOVsd_RR(reg,dst));
addInstr(env, X86Instr_Alu32R(aluOp, rmi, dst));
return dst;
}
libx86emu
libx86emu is a small library to emulate x86 instructions.
A sample for add instruction: https://github.com/wfeldt/libx86emu/prim_ops.c
u8 add_byte(x86emu_t *emu, u8 d, u8 s)
{
register u32 res; /* all operands in native machine order */
register u32 cc;
res = d + s;
CONDITIONAL_SET_FLAG(res & 0x100, F_CF);
CONDITIONAL_SET_FLAG((res & 0xff) == 0, F_ZF);
CONDITIONAL_SET_FLAG(res & 0x80, F_SF);
CONDITIONAL_SET_FLAG(PARITY(res & 0xff), F_PF);
/* calculate the carry chain SEE NOTE AT TOP. */
cc = (s & d) | ((~res) & (s | d));
CONDITIONAL_SET_FLAG(XOR2(cc >> 6), F_OF);
CONDITIONAL_SET_FLAG(cc & 0x8, F_AF);
return (u8)res;
}
Blink
blink is a virtual machine that runs x86-64-linux programs, or tiniest x86-64-linux emulator. Its written in C, and claiming faster that Qemu.
A sample for add instruction: https://github.com/jart/blink/blink/alu.c
i64 Add64(struct Machine *m, u64 x, u64 y) {
u64 z;
bool cf, of, af;
z = x + y;
cf = z < y;
af = (z & 15) < (y & 15);
of = ((z ^ x) & (z ^ y)) >> 63;
return AluFlags64(m, z, af, of, cf);
}
It has JIT engine, on the implmentation (depend on host) is: https://github.com/jart/blink/blink/uop.c
#if X86_INTRINSICS
#define ALU_FAST(M, TYPE, X, Y, OP, COMMUT) \
({ \
TYPE Res; \
u32 OldFlags = (M)->flags & ~(OF | SF | ZF | AF | CF); \
u64 NewFlags; \
asm(OP "\t%3,%0\n\t" \
"pushfq\n\t" \
"pop\t%1" \
: "=&r" (Res), "=r" (NewFlags) \
: COMMUT "0" ((TYPE)(X)), "g" ((TYPE)(Y)) \
: "cc"); \
(M)->flags = OldFlags | ((u32)NewFlags & (OF | SF | ZF | AF | CF)); \
Res; \
})
Fex
FEX allows you to run x86 and x86-64 binaries on an AArch64 host, similar to qemu and box. Its written in C++.
Fex using JIT, the code emitter fo add instruction: https://github.com/FEX-Emu/FEX/CodeEmitter/CodeEmitter/ALUOps.inl
void add(ARMEmitter::Size s, ARMEmitter::Register rd, ARMEmitter::Register rn, uint32_t Imm, bool LSL12 = false) {
constexpr uint32_t Op = 0b0001'0001'0 << 23;
DataProcessing_AddSub_Imm(Op, s, rd, rn, Imm, LSL12);
}
It has good documentation to understand the source code thru: SourceOutline
x86emu (shift-Corps)
x86emu is Simple x86 emulator, written in C++.
A sample for add instruction: https://github.com/shift-crops/x86emu/instruction/instr32.cpp
void Instr32::add_rm32_r32(void){
uint32_t rm32, r32;
rm32 = get_rm32();
r32 = get_r32();
set_rm32(rm32+r32);
EFLAGS_UPDATE_ADD(rm32, r32);
}
Halfix
halfix is x86 PC emulator that runs both natively and in the browser. It is written in C.
A sample of add instruction: https://github.com/nepx/halfix/src/cpu/ops/arith.c
case 0: // ADD
cpu.lop2 = src;
cpu.lr = (int8_t)(*dest += src);
cpu.laux = ADD8;
break;
Niwaka X86 Emulator 2
X86 Emulator 2 (Niwaka) is x86/emulator written in C++.
A sample of add instruction: https://github.com/NiwakaDev/X86_EMULATOR_2/src/Instruction.cpp
void AddRm32R32::Run(Cpu& cpu, Memory& memory) {
cpu.AddEip(1);
this->ParseModRM(cpu, memory);
if (cpu.Is32bitsMode() ^ cpu.IsPrefixOpSize()) {
uint32_t rm32 = this->GetRM32(cpu, memory);
uint32_t r32 =
cpu.GetR32((GENERAL_PURPOSE_REGISTER32)this->modrm.reg_index);
uint64_t result = rm32 + r32;
this->SetRM32(cpu, memory, cpu.Add(rm32, r32));
return;
}
uint16_t rm16 = this->GetRM16(cpu, memory);
uint16_t r16 = cpu.GetR16((GENERAL_PURPOSE_REGISTER32)this->modrm.reg_index);
this->SetRM16(cpu, memory, cpu.Add(rm16, r16));
return;
}
v86
v86 is x86 PC emulator and x86-to-wasm JIT. It is written in Rust.
A sample of add instruction: https://github.com/copy/v86/src/rust/cpu/arith.rs
unsafe fn add(dest_operand: i32, source_operand: i32, op_size: i32) -> i32 {
let res = dest_operand + source_operand;
*last_op1 = dest_operand;
*last_result = res & (2 << op_size) - 1;
*last_op_size = op_size;
*flags_changed = FLAGS_ALL;
return res;
}
The JIT to WASM opcode for add instruction: https://github.com/copy/v86/src/rust/jit_instructions.rs
fn gen_add8(ctx: &mut JitContext, dest_operand: &WasmLocal, source_operand: &LocalOrImmediate) {
ctx.current_instruction = Instruction::Add {
opsize: OPSIZE_8,
dest: local_to_instruction_operand(ctx, dest_operand),
source: if source_operand.eq_local(dest_operand) {
InstructionOperand::Other // aliasing
}
else {
source_operand.to_instruction_operand(ctx)
},
is_inc: false,
};
ctx.builder.const_i32(global_pointers::last_op1 as i32);
ctx.builder.get_local(dest_operand);
ctx.builder.const_i32(0xFF);
ctx.builder.and_i32();
ctx.builder.store_aligned_i32(0);
ctx.builder.const_i32(global_pointers::last_result as i32);
ctx.builder.get_local(dest_operand);
source_operand.gen_get(ctx.builder);
ctx.builder.add_i32();
ctx.builder.const_i32(0xFF);
ctx.builder.and_i32();
ctx.builder.store_aligned_i32(0);
codegen::gen_set_last_op_size_and_flags_changed(ctx.builder, OPSIZE_8, FLAGS_ALL);
ctx.builder
.load_fixed_u8(global_pointers::last_result as u32);
}
86Box
86Box is a low level x86 emulator that runs older operating systems and software. It is written in C.
A sample for its add instruction: https://github.com/86Box/86Box/src/cpu/x86_ops_arith.h
OP_ARITH(ADD, dst + src, setadd, (dst, src), 0)
It has dynarec/JIT, for add instruction on x86 host: https://github.com/86Box/86Box/src/codegen_new/codegen_ops_arith.c
uint32_t
ropADD_w_rm(codeblock_t *block, ir_data_t *ir, UNUSED(uint8_t opcode), uint32_t fetchdat, uint32_t op_32, uint32_t op_pc)
{
int dest_reg = (fetchdat >> 3) & 7;
codegen_mark_code_present(block, cs + op_pc, 1);
if ((fetchdat & 0xc0) == 0xc0) {
int src_reg = fetchdat & 7;
uop_MOVZX(ir, IREG_flags_op1, IREG_16(dest_reg));
uop_MOVZX(ir, IREG_flags_op2, IREG_16(src_reg));
uop_ADD(ir, IREG_16(dest_reg), IREG_16(dest_reg), IREG_16(src_reg));
} else {
x86seg *target_seg;
uop_MOV_IMM(ir, IREG_oldpc, cpu_state.oldpc);
target_seg = codegen_generate_ea(ir, op_ea_seg, fetchdat, op_ssegs, &op_pc, op_32, 0);
codegen_check_seg_read(block, ir, target_seg);
uop_MEM_LOAD_REG(ir, IREG_temp0_W, ireg_seg_base(target_seg), IREG_eaaddr);
uop_MOVZX(ir, IREG_flags_op1, IREG_16(dest_reg));
uop_MOVZX(ir, IREG_flags_op2, IREG_temp0_W);
uop_ADD(ir, IREG_16(dest_reg), IREG_16(dest_reg), IREG_temp0_W);
}
uop_MOV_IMM(ir, IREG_flags_op, FLAGS_ADD16);
uop_MOVZX(ir, IREG_flags_res, IREG_16(dest_reg));
codegen_flags_changed = 1;
return op_pc + 1;
}
X86Emulator
No description, but it seems being used for malware analysis, it also has any of windows APIs. It is written in C++.
A sample for add instruction: https://github.com/AmrThabet/x86Emulator/emu/math.cpp
int op_add(Thread & thread, DISASM_INSTRUCTION * s) {
// first we will test the source and get the value that we will put in the dest in src variable
DWORD dest, result;
int src = 0;
...
// now we have the value of the src that we will put it in the dest now we will test the dest
if (s->flags & DEST_REG) {
dest = thread.Exx[s->ndest];
if (s->flags & DEST_BITS32) {
src += thread.Exx[s->ndest];
memcpy(&thread.Exx[s->ndest], &src, 4);
result = thread.Exx[s->ndest];
}
...
}
...
}
x86emu (cseagle)
x86emu is Embedded x86 emulator for Ida Pro.
A sample for its add instruction: https://github.com/cseagle/x86emu/cpu.cpp
unsigned int add(unsigned long long op1, unsigned int op2) {
unsigned int mask = SIZE_MASKS[opsize];
unsigned long long result = (op1 & mask) + (op2 & mask);
if (result & CARRY_BITS[opsize]) SET(xCF);
else CLEAR(xCF);
checkAddOverflow((unsigned int)op1, op2, (unsigned int)result);
setEflags(result, opsize);
checkAuxCarry((unsigned int)op1, (unsigned int)op2, (unsigned int)result);
return (unsigned int) result & mask;
}
MartyPC
MartyPC is An IBM PC/XT emulator written in Rust.
A sample of add instruction: https://github.com/dbalsom/martypc/core/src/cpu_808x/alu.rs
Mnemonic::ADD => {
let (result, carry, overflow, aux_carry) = operand1.alu_add(operand2);
self.set_flag_state(Flag::Carry, carry);
self.set_flag_state(Flag::Overflow, overflow);
self.set_flag_state(Flag::AuxCarry, aux_carry);
self.set_szp_flags_from_result_u8(result);
result
}
Aeon
Aeon is C# x86/DOS emulator.
A sample of add instruction: https://github.com/gregdivis/Aeon/src/Aeon.Emulator/Instructions/Arithmetic/Add.cs
public static void ByteAdd(Processor p, ref byte dest, byte src)
{
uint uResult = (uint)dest + (uint)src;
p.Flags.Update_Add_Byte(dest, src, (byte)uResult);
dest = (byte)uResult;
}
Jemul8
Jemul8 is an object-oriented JavaScript x86 Emulator for Node.js and the browser.
A sample of add instruction: https://github.com/asmblah/jemul8/js/core/classes/cpu/execute.js
/ Arithmetic Addition
}, "ADD": function (cpu) {
var val1 = this.operand1.read(),
val2 = this.operand2.read(),
// Mask, because add operation can generate too-large numbers
res = (val1 + val2) & this.operand1.mask;
this.operand1.write(res);
setFlags(this, cpu, val1, val2, res);
dax86
dax86 is xv6-runnable (memfs) x86 Emulator.
A sample of add instruction: https://github.com/ykskb/dax86/instructions_00.c
void add_rm8_r8(Emulator *emu)
{
emu->eip += 1;
ModRM modrm = create_modrm();
parse_modrm(emu, &modrm);
uint8_t rm8_val = get_rm8(emu, &modrm);
uint8_t r8_val = get_r8(emu, &modrm);
uint16_t result = (uint16_t)rm8_val + (uint16_t)r8_val;
set_rm8(emu, &modrm, result);
update_eflags_add_8bit(emu, rm8_val, r8_val, result);
}
TowerPC
TowerPC is An x86 PC emulator, written in C++ around 2007.
A sample of add instruction: https://github.com/whatisaphone/tower-pc/x86/math_helpers.hpp
inline int8 Xi__(add8)(int8 op1, int8 op2) {
int8 res = op1 + op2;
*eflags = *eflags & ~flags_mask_cpazso |
(res < op1 ? flags_mask_cf : 0) |
parity_lookup[res] | // pf
((res ^ op1 ^ op2) & 0x10) | // af
(res ? 0 : flags_mask_zf) |
(res & 0x80) | // sf
((res ^ op1) & (res ^ op2) & 0x80 ? flags_mask_of : 0);
return res;
}
jPC
jPC is A x86 PC emulator written in Java.
A sample of add instruction: https://github.com/h0MER247/jPC/src/Hardware/CPU/Intel80386/Instructions/i386/Arithmetic/ADD16.java
public void run() {
int dest = m_destination.getValue();
int src = m_source.getValue();
int result = (dest + src) & 0xffff;
m_cpu.FLAGS.setSZP16(result);
m_cpu.FLAGS.CF = Integer.compareUnsigned(result, dest) < 0;
m_cpu.FLAGS.OF = (((dest ^ src ^ 0x8000) & (result ^ src)) & 0x8000) != 0;
m_cpu.FLAGS.AF = (((dest ^ src) ^ result) & 0x10) != 0;
m_destination.setValue(result);
}
x64emu (sdvassilev)
x64emu is a light-weight and stripped-down IA-32e software emulator, built
as a static library
A sample of add instruction: https://github.com/sdvassilev/x64emu/x64emu/x64emu.cpp
template <class T>
void ADD_Helper(X64_EMULATOR_CTX* pEmu)
{
DECODED_INSTRUCTION& instr=pEmu->Instruction;
RFLAGS& rfl = pEmu->CpuState.Rflags;
T op1 = *reinterpret_cast<T*>(instr.Operands[0].Op);
T op2 = static_cast<T>(instr.Operands[1].OpAsInt64);
const T& res = *reinterpret_cast<T*>(instr.Operands[0].Op) = op1 + op2;
SetStatusFlagsAfterAdd(rfl, op1, op2, 0, res);
}
ThreeAteSix
ThreeAteSix is a 386 emulator written in Go.
A sample for add instruction: https://github.com/andrewjc/threeatesix/devices/intel8086/arithmetic_32.go
func INSTR_ADD_RM32(core *CpuCore, modrm ModRm, immediate uint32) {
if core.Is32BitOperand() {
src := &immediate
srcName := fmt.Sprintf("0x%08x", immediate)
dest, destName, err := core.readRm32(&modrm)
if err != nil {
core.dumpAndExit()
}
*dest += *src
core.registers.SetFlag(ZeroFlag, *dest == 0)
core.registers.SetFlag(SignFlag, (*dest>>31)&0x01 == 1)
core.registers.SetFlag(OverFlowFlag, false) // Assume no overflow for ADD
core.logInstruction(fmt.Sprintf("[%#04x] %s %s, %s", core.GetCurrentlyExecutingInstructionAddress(), "ADD", destName, srcName))
} else {
tinyx86
tinyx86 is a simple emulator with the goal of emulating the Intel 80836
A sample of its add instruction: https://github.com/PoisonNinja/tinyx86/hw/cpu/arithmetic.c
void cpu_arithmetic_add_u8(struct cpu* cpu, uint8_t* dest, uint8_t* src)
{
cpu->last_op1 = *dest;
cpu->last_op2 = *src;
cpu->last_result = *dest = *dest + *src;
cpu->last_size = CPU_OP_SIZE_8;
cpu->eflags_dirty = CPU_EFLAGS_ALL;
}
Enzan
Enzan is an x86 emulator written in C#
A sample of add instruction: https://github.com/ycanardeau/Enzan/Aigamo.Enzan/Cpu.cs
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public Register32 Add(Register32 left, Register32 right)
{
var tmp = new Register64((ulong)left.Value + (ulong)right.Value);
var result = tmp.Low;
Eflags.Update(carry: !tmp.High.IsEmpty, zero: result.IsEmpty, sign: result.Sign, overflow: left.Sign == right.Sign && left.Sign != result.Sign);
return result;
}
Pce
Pce is PC simulator/emulator targeting a range of systems from 8086 through to Pentium, written in C++.
Pce was authored by Duckstation (Fast PlayStation 1 emulator for x86-64/AArch32/AArch64/RV64) creator.
A sample of add instruction:
https://github.com/stenzek/pce/src/pce/cpu_x86/interpreter.inl
ALWAYS_INLINE u8 ALUOp_Add8(u32* eflags, u8 lhs, u8 rhs)
{
const u32 old_value = ZeroExtend32(lhs);
const u32 add_value = ZeroExtend32(rhs);
const u32 new_value = old_value + add_value;
const u8 out_value = Truncate8(new_value);
*eflags = EFLAGS_ALUAdd8(*eflags, old_value, add_value, new_value, out_value);
return out_value;
}
NOTES
Please tell me, if there were some implementations not mention before.