""" x86_core.py -- i386/i486 user-mode integer core (flat 32-bit protected mode), every data transformation routed through the x86_units API (golden or neural). Scope: the instruction set a -march=i486 -O1 musl/doomgeneric binary uses. No FPU (DOOM is fixed-point), no paging (flat), no real mode. Segments are flat except %gs, whose base is honored for i386 TLS. Composition stances (consistent with the GB bricks): - all arithmetic/logic/shift/mul/div through verified slice units (ALU class) - effective-address arithmetic, immediate assembly, and dispatch are wiring - one-operand IMUL/MUL produce edx:eax via the 8x8 partial-product tree; IDIV/DIV are restoring division over verified subtract slices """ from x86_units import GoldenUnits, ALU EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI = range(8) CF, PF, AF, ZF, SF, OF, DF = "CF", "PF", "AF", "ZF", "SF", "OF", "DF" M32 = 0xFFFFFFFF def sx8(v): return v - 0x100 if v & 0x80 else v def sx16(v): return v - 0x10000 if v & 0x8000 else v def sx32(v): return v - 0x100000000 if v & 0x80000000 else v class CPUError(Exception): pass class X86: def __init__(self, mem, units=None): self.mem = mem # bytearray, flat self.u = units or GoldenUnits() self.alu = ALU(self.u) self.r = [0] * 8 self.eip = 0 self.f = {CF: 0, PF: 0, AF: 0, ZF: 0, SF: 0, OF: 0, DF: 0} self.gs_base = 0 self.exited = None # set to status on exit syscall self.syscall = None # callback(cpu) for int 0x80 self.instr_count = 0 # ---------------- memory (wiring) ---------------- def rd(self, a, n): a &= M32 return int.from_bytes(self.mem[a:a + n], "little") def wr(self, a, v, n): a &= M32 self.mem[a:a + n] = int(v & ((1 << (8 * n)) - 1)).to_bytes(n, "little") def fetch(self, n=1): v = self.rd(self.eip, n) self.eip = (self.eip + n) & M32 return v # ---------------- register access (wiring) ---------------- def get_r(self, i, size): if size == 32: return self.r[i] if size == 16: return self.r[i] & 0xFFFF if i < 4: return self.r[i] & 0xFF # AL CL DL BL return (self.r[i - 4] >> 8) & 0xFF # AH CH DH BH def set_r(self, i, v, size): if size == 32: self.r[i] = v & M32 elif size == 16: self.r[i] = (self.r[i] & 0xFFFF0000) | (v & 0xFFFF) elif i < 4: self.r[i] = (self.r[i] & ~0xFF) | (v & 0xFF) else: self.r[i - 4] = (self.r[i - 4] & ~0xFF00) | ((v & 0xFF) << 8) # ---------------- flags ---------------- def setf(self, fl): for k, v in fl.items(): self.f[k] = int(v) def eflags(self): f = self.f return (f[CF] | (f[PF] << 2) | (f[AF] << 4) | (f[ZF] << 6) | (f[SF] << 7) | (f[DF] << 10) | (f[OF] << 11) | 0x2) def set_eflags(self, v): self.f[CF] = v & 1; self.f[PF] = (v >> 2) & 1; self.f[AF] = (v >> 4) & 1 self.f[ZF] = (v >> 6) & 1; self.f[SF] = (v >> 7) & 1 self.f[DF] = (v >> 10) & 1; self.f[OF] = (v >> 11) & 1 def cond(self, cc): f = self.f v = [f[OF], f[CF], f[ZF], f[CF] | f[ZF], f[SF], f[PF], f[SF] ^ f[OF], (f[SF] ^ f[OF]) | f[ZF]][cc >> 1] return v ^ (cc & 1) # ---------------- ModRM / SIB (decode units + EA wiring) ---------------- def modrm(self, size, seg_base=0): mod, reg, rm = self.u.modrm(self.fetch()) if mod == 3: return reg, ("r", rm) if rm == 4: ss, idx, base = self.u.sib(self.fetch()) ea = 0 if (base == 5 and mod == 0) else self.r[base] if base == 5 and mod == 0: ea = self.fetch(4) if idx != 4: ea = (ea + (self.r[idx] << ss)) & M32 elif rm == 5 and mod == 0: ea = self.fetch(4) else: ea = self.r[rm] if mod == 1: ea = (ea + sx8(self.fetch())) & M32 elif mod == 2: ea = (ea + self.fetch(4)) & M32 return reg, ("m", (ea + seg_base) & M32) def get_rm(self, loc, size): k, x = loc return self.get_r(x, size) if k == "r" else self.rd(x, size // 8) def set_rm(self, loc, v, size): k, x = loc if k == "r": self.set_r(x, v, size) else: self.wr(x, v, size // 8) # ---------------- ALU dispatch through units ---------------- def alu_op(self, op, a, b, size): """op: 0 ADD,1 OR,2 ADC,3 SBB,4 AND,5 SUB,6 XOR,7 CMP. Returns result or None.""" if op == 0: r, fl = self.alu.addsub(0, a, b, size) elif op == 2: r, fl = self.alu.addsub(0, a, b, size, self.f[CF]) elif op in (5, 7): r, fl = self.alu.addsub(1, a, b, size) elif op == 3: r, fl = self.alu.addsub(1, a, b, size, self.f[CF]) elif op == 1: r, fl = self.alu.logic("OR", a, b, size) elif op == 4: r, fl = self.alu.logic("AND", a, b, size) else: r, fl = self.alu.logic("XOR", a, b, size) self.setf(fl) return None if op == 7 else r def incdec(self, v, size, dec): r, fl = self.alu.addsub(dec, v, 1, size) fl.pop("CF") # INC/DEC preserve CF self.setf(fl) return r # ---------------- shifts/rotates: 1-bit verified slices, k times ---------------- def shift(self, kind, v, count, size): count &= 31 if count == 0: return v msb = 1 << (size - 1) orig_msb = 1 if v & msb else 0 for i in range(count): if kind == 4: # SHL v, c = self.alu.shl1(v, size) elif kind == 5: # SHR v, c = self.alu.shr1(v, size) elif kind == 7: # SAR v, c = self.alu.shr1(v, size, top=1 if v & msb else 0) elif kind == 0: # ROL v, c = self.alu.shl1(v, size) if c: v |= 1 elif kind == 1: # ROR v, c = self.alu.shr1(v, size, top=v & 1) elif kind == 2: # RCL v, nc = self.alu.shl1(v, size) if self.f[CF]: v |= 1 c = nc else: # RCR v, c = self.alu.shr1(v, size, top=self.f[CF]) self.f[CF] = c if kind in (4, 5, 7): SF_, ZF_, PF_ = self.alu.flags_of(v, size) self.f[SF], self.f[ZF], self.f[PF] = SF_, ZF_, PF_ if count == 1: if kind == 4: self.f[OF] = ((v >> (size - 1)) & 1) ^ self.f[CF] elif kind == 5: self.f[OF] = orig_msb elif kind == 7: self.f[OF] = 0 elif kind == 0: self.f[OF] = ((v >> (size - 1)) & 1) ^ self.f[CF] elif kind == 1: self.f[OF] = ((v >> (size - 1)) ^ (v >> (size - 2))) & 1 return v def shd(self, left, v, fill, count, size): """SHLD/SHRD: double shift through verified 1-bit slices over 2*size bits.""" count &= 31 if count == 0: return v if left: wide = (v << size) | fill for _ in range(count): wide, c = ALU.shl1(self.alu, wide, 2 * size) if False else self.alu.shl1(wide, 2 * size) self.f[CF] = c v = (wide >> size) & ((1 << size) - 1) else: wide = (fill << size) | v for _ in range(count): wide, c = self.alu.shr1(wide, 2 * size) self.f[CF] = c v = wide & ((1 << size) - 1) SF_, ZF_, PF_ = self.alu.flags_of(v, size) self.f[SF], self.f[ZF], self.f[PF] = SF_, ZF_, PF_ return v # ---------------- stack (wiring over mem) ---------------- def push(self, v): self.r[ESP] = (self.r[ESP] - 4) & M32 self.wr(self.r[ESP], v, 4) def pop(self): v = self.rd(self.r[ESP], 4) self.r[ESP] = (self.r[ESP] + 4) & M32 return v # ---------------- one instruction ---------------- def step(self): self.instr_count += 1 start = self.eip osize = 32; seg = 0 # prefixes (decode unit) rep = None while True: b = self.rd(self.eip, 1) isp, cls = self.u.prefix(b) if not isp: break self.eip = (self.eip + 1) & M32 if cls == 1: osize = 16 elif cls == 4: rep = "ne" elif cls == 5: rep = "e" elif cls == 6 and b == 0x65: seg = self.gs_base op = self.fetch() try: self.exec_op(op, osize, seg, rep) except CPUError: self.eip = start raise def exec_op(self, op, osize, seg, rep): S = osize # ---- ALU group 00-3D ---- if op < 0x40 and (op & 7) < 6: kind = op >> 3 form = op & 7 if form in (0, 1): # r/m, r size = 8 if form == 0 else S reg, loc = self.modrm(size, seg) r = self.alu_op(kind, self.get_rm(loc, size), self.get_r(reg, size), size) if r is not None: self.set_rm(loc, r, size) elif form in (2, 3): # r, r/m size = 8 if form == 2 else S reg, loc = self.modrm(size, seg) r = self.alu_op(kind, self.get_r(reg, size), self.get_rm(loc, size), size) if r is not None: self.set_r(reg, r, size) else: # AL/eAX, imm size = 8 if form == 4 else S imm = self.fetch(size // 8) r = self.alu_op(kind, self.get_r(EAX, size), imm, size) if r is not None: self.set_r(EAX, r, size) return # ---- INC/DEC r32 40-4F ---- if 0x40 <= op <= 0x4F: i = op & 7 self.set_r(i, self.incdec(self.get_r(i, S), S, (op >> 3) & 1), S) return # ---- PUSH/POP r32 50-5F ---- if 0x50 <= op <= 0x57: self.push(self.get_r(op & 7, 32)); return if 0x58 <= op <= 0x5F: self.set_r(op & 7, self.pop(), 32); return if op == 0x68: self.push(self.fetch(4)); return if op == 0x6A: self.push(sx8(self.fetch()) & M32); return if op == 0x8F: _, loc = self.modrm(S, seg); self.set_rm(loc, self.pop(), 32); return # ---- IMUL r,r/m,imm (69/6B) ---- if op in (0x69, 0x6B): reg, loc = self.modrm(S, seg) a = sx32(self.get_rm(loc, S)) if S == 32 else sx16(self.get_rm(loc, S)) b = sx8(self.fetch()) if op == 0x6B else ( sx32(self.fetch(4)) if S == 32 else sx16(self.fetch(2))) full = self.alu.mul(abs(a), abs(b), S) if (a < 0) != (b < 0): full = (-full) & ((1 << (2 * S)) - 1) res = full & ((1 << S) - 1) sres = sx32(res) if S == 32 else sx16(res) ovf = int(sx32(full if S == 32 else 0) != a * b) if S == 32 else 0 ovf = int(((full ^ ((res >> (S-1)) * ((1 << (2*S)) - (1 << S)))) >> S) != 0) self.f[CF] = self.f[OF] = ovf self.set_r(reg, res, S) return # ---- Jcc short 70-7F ---- if 0x70 <= op <= 0x7F: d = sx8(self.fetch()) if self.cond(op & 0xF): self.eip = (self.eip + d) & M32 return # ---- group 80/81/83 ---- if op in (0x80, 0x81, 0x83): size = 8 if op == 0x80 else S reg, loc = self.modrm(size, seg) imm = (self.fetch() if op == 0x80 else sx8(self.fetch()) & ((1 << size) - 1) if op == 0x83 else self.fetch(size // 8)) r = self.alu_op(reg, self.get_rm(loc, size), imm, size) if r is not None: self.set_rm(loc, r, size) return # ---- TEST 84/85 ---- if op in (0x84, 0x85): size = 8 if op == 0x84 else S reg, loc = self.modrm(size, seg) self.alu_op(4, self.get_rm(loc, size), self.get_r(reg, size), size) return # ---- XCHG 86/87 ---- if op in (0x86, 0x87): size = 8 if op == 0x86 else S reg, loc = self.modrm(size, seg) a, b = self.get_rm(loc, size), self.get_r(reg, size) self.set_rm(loc, b, size); self.set_r(reg, a, size) return # ---- MOV 88-8B ---- if 0x88 <= op <= 0x8B: size = 8 if op in (0x88, 0x8A) else S reg, loc = self.modrm(size, seg) if op <= 0x89: self.set_rm(loc, self.get_r(reg, size), size) else: self.set_r(reg, self.get_rm(loc, size), size) return if op in (0x8C, 0x8E): # MOV sreg (flat segments: no-op) _, loc = self.modrm(16, seg) if op == 0x8C: self.set_rm(loc, 0, 16) return if op == 0x8D: # LEA reg, loc = self.modrm(S, seg) assert loc[0] == "m" self.set_r(reg, loc[1], S) return if op == 0x90: return # NOP if 0x91 <= op <= 0x97: # XCHG eAX, r i = op & 7 a = self.get_r(EAX, S); self.set_r(EAX, self.get_r(i, S), S); self.set_r(i, a, S) return if op == 0x98: # CWDE self.set_r(EAX, sx16(self.get_r(EAX, 16)) & M32, 32); return if op == 0x99: # CDQ self.set_r(EDX, (M32 if self.r[EAX] & 0x80000000 else 0), 32); return if op == 0x9C: self.push(self.eflags()); return # PUSHFD if op == 0x9D: self.set_eflags(self.pop()); return # ---- MOV moffs A0-A3 ---- if op in (0xA0, 0xA1): a = self.fetch(4) + seg self.set_r(EAX, self.rd(a, 1 if op == 0xA0 else S // 8), 8 if op == 0xA0 else S) return if op in (0xA2, 0xA3): a = self.fetch(4) + seg self.wr(a, self.get_r(EAX, 8 if op == 0xA2 else S), 1 if op == 0xA2 else S // 8) return # ---- string ops ---- if op in (0xA4, 0xA5, 0xAA, 0xAB, 0xA6, 0xA7, 0xAE, 0xAF, 0xAC, 0xAD): self.string_op(op, S, rep) return if op in (0xA8, 0xA9): # TEST AL/eAX, imm size = 8 if op == 0xA8 else S self.alu_op(4, self.get_r(EAX, size), self.fetch(size // 8), size) return # ---- MOV r, imm B0-BF ---- if 0xB0 <= op <= 0xB7: self.set_r(op & 7, self.fetch(), 8); return if 0xB8 <= op <= 0xBF: self.set_r(op & 7, self.fetch(S // 8), S); return # ---- shift groups C0/C1/D0/D1/D3 ---- if op in (0xC0, 0xC1, 0xD0, 0xD1, 0xD2, 0xD3): size = 8 if op in (0xC0, 0xD0, 0xD2) else S kind, loc = self.modrm(size, seg) cnt = (self.fetch() if op in (0xC0, 0xC1) else 1 if op in (0xD0, 0xD1) else self.get_r(ECX, 8)) self.set_rm(loc, self.shift(kind, self.get_rm(loc, size), cnt, size), size) return if op == 0xC2: n = self.fetch(2); self.eip = self.pop() self.r[ESP] = (self.r[ESP] + n) & M32; return if op == 0xC3: self.eip = self.pop(); return if op in (0xC6, 0xC7): # MOV r/m, imm size = 8 if op == 0xC6 else S _, loc = self.modrm(size, seg) self.set_rm(loc, self.fetch(size // 8), size) return if op == 0xC9: # LEAVE self.r[ESP] = self.r[EBP]; self.r[EBP] = self.pop(); return if op == 0xCD: # INT imm n = self.fetch() if n == 0x80 and self.syscall: self.syscall(self) return raise CPUError(f"INT {n:#x}") # ---- group F6/F7: TEST/NOT/NEG/MUL/IMUL/DIV/IDIV ---- if op in (0xF6, 0xF7): size = 8 if op == 0xF6 else S kind, loc = self.modrm(size, seg) v = self.get_rm(loc, size) if kind in (0, 1): self.alu_op(4, v, self.fetch(size // 8), size) elif kind == 2: self.set_rm(loc, self.alu.not_(v, size), size) elif kind == 3: # NEG r, fl = self.alu.addsub(1, 0, v, size) self.setf(fl); self.set_rm(loc, r, size) else: self.muldiv(kind, v, size) return if op == 0xF8: self.f[CF] = 0; return # CLC if op == 0xF9: self.f[CF] = 1; return # STC if op == 0xFC: self.f[DF] = 0; return # CLD if op == 0xFD: self.f[DF] = 1; return # STD # ---- group FE/FF ---- if op in (0xFE, 0xFF): size = 8 if op == 0xFE else S kind, loc = self.modrm(size, seg) if kind in (0, 1): self.set_rm(loc, self.incdec(self.get_rm(loc, size), size, kind), size) elif kind == 2: self.push(self.eip); self.eip = self.get_rm(loc, 32) elif kind == 4: self.eip = self.get_rm(loc, 32) elif kind == 6: self.push(self.get_rm(loc, 32)) else: raise CPUError(f"FF/{kind}") return if op == 0xE8: # CALL rel32 d = sx32(self.fetch(4)); self.push(self.eip) self.eip = (self.eip + d) & M32; return if op == 0xE9: d = sx32(self.fetch(4)); self.eip = (self.eip + d) & M32; return if op == 0xEB: d = sx8(self.fetch()); self.eip = (self.eip + d) & M32; return # ---- 0F escape ---- if op == 0x0F: self.exec_0f(self.fetch(), S, seg) return raise CPUError(f"opcode {op:#04x}") def exec_0f(self, op, S, seg): if 0x80 <= op <= 0x8F: # Jcc rel32 d = sx32(self.fetch(4)) if self.cond(op & 0xF): self.eip = (self.eip + d) & M32 return if 0x90 <= op <= 0x9F: # SETcc _, loc = self.modrm(8, seg) self.set_rm(loc, 1 if self.cond(op & 0xF) else 0, 8) return if op in (0xB6, 0xB7, 0xBE, 0xBF): # MOVZX/MOVSX ssize = 8 if op in (0xB6, 0xBE) else 16 reg, loc = self.modrm(ssize, seg) v = self.get_rm(loc, ssize) if op >= 0xBE: v = (sx8(v) if ssize == 8 else sx16(v)) & M32 self.set_r(reg, v, S) return if op == 0xAF: # IMUL r, r/m reg, loc = self.modrm(S, seg) a = sx32(self.get_r(reg, S)); b = sx32(self.get_rm(loc, S)) full = self.alu.mul(abs(a), abs(b), S) if (a < 0) != (b < 0): full = (-full) & ((1 << (2 * S)) - 1) res = full & M32 self.f[CF] = self.f[OF] = int(sx32(res) != a * b) self.set_r(reg, res, S) return if op in (0xA4, 0xA5): # SHLD reg, loc = self.modrm(S, seg) cnt = self.fetch() if op == 0xA4 else self.get_r(ECX, 8) self.set_rm(loc, self.shd(1, self.get_rm(loc, S), self.get_r(reg, S), cnt, S), S) return if op in (0xAC, 0xAD): # SHRD reg, loc = self.modrm(S, seg) cnt = self.fetch() if op == 0xAC else self.get_r(ECX, 8) self.set_rm(loc, self.shd(0, self.get_rm(loc, S), self.get_r(reg, S), cnt, S), S) return if op == 0xBC or op == 0xBD: # BSF/BSR reg, loc = self.modrm(S, seg) v = self.get_rm(loc, S) self.f[ZF] = int(v == 0) if v: idx = (v & -v).bit_length() - 1 if op == 0xBC else v.bit_length() - 1 self.set_r(reg, idx, S) return if op == 0xA3 or op == 0xAB or op == 0xB3 or op == 0xBA: # BT/BTS/BTR (+grp8) if op == 0xBA: kind, loc = self.modrm(S, seg) bit = self.fetch() & (S - 1) else: reg, loc = self.modrm(S, seg) bit = self.get_r(reg, S) & (S - 1) kind = {0xA3: 4, 0xAB: 5, 0xB3: 6}[op] v = self.get_rm(loc, S) self.f[CF] = (v >> bit) & 1 if kind == 5: self.set_rm(loc, v | (1 << bit), S) elif kind == 6: self.set_rm(loc, v & ~(1 << bit), S) return if op in (0xB0, 0xB1): # CMPXCHG size = 8 if op == 0xB0 else S reg, loc = self.modrm(size, seg) dst = self.get_rm(loc, size); acc = self.get_r(EAX, size) self.alu_op(7, acc, dst, size) # CMP semantics for flags if self.f[ZF]: self.set_rm(loc, self.get_r(reg, size), size) else: self.set_r(EAX, dst, size) return if op in (0xC0, 0xC1): # XADD size = 8 if op == 0xC0 else S reg, loc = self.modrm(size, seg) a, b = self.get_rm(loc, size), self.get_r(reg, size) r = self.alu_op(0, a, b, size) self.set_r(reg, a, size); self.set_rm(loc, r, size) return if 0xC8 <= op <= 0xCF: # BSWAP i = op & 7; v = self.r[i] self.r[i] = int.from_bytes(v.to_bytes(4, "little"), "big") return if op == 0xA2: # CPUID self.r[EAX] = self.r[EBX] = self.r[ECX] = self.r[EDX] = 0 return if 0x40 <= op <= 0x4F: # CMOVcc reg, loc = self.modrm(S, seg) v = self.get_rm(loc, S) if self.cond(op & 0xF): self.set_r(reg, v, S) return raise CPUError(f"0F {op:#04x}") # ---------------- MUL/IMUL/DIV/IDIV (edx:eax forms) ---------------- def muldiv(self, kind, v, size): a = self.get_r(EAX, size) if kind == 4: # MUL full = self.alu.mul(a, v, size) lo = full & ((1 << size) - 1); hi = full >> size self.set_r(EAX, lo, size) if size == 8: self.set_r(EAX, full, 16) else: self.set_r(EDX, hi, size) self.f[CF] = self.f[OF] = int(hi != 0) elif kind == 5: # IMUL sa = sx8(a) if size == 8 else sx16(a) if size == 16 else sx32(a) sv = sx8(v) if size == 8 else sx16(v) if size == 16 else sx32(v) full = self.alu.mul(abs(sa), abs(sv), size) if (sa < 0) != (sv < 0): full = (-full) & ((1 << (2 * size)) - 1) lo = full & ((1 << size) - 1); hi = full >> size if size == 8: self.set_r(EAX, full, 16) else: self.set_r(EAX, lo, size); self.set_r(EDX, hi, size) sign_ext = ((1 << size) - 1) if lo >> (size - 1) else 0 self.f[CF] = self.f[OF] = int(hi != sign_ext) else: # DIV/IDIV if size == 8: num = self.get_r(EAX, 16) else: num = (self.get_r(EDX, size) << size) | a if kind == 6: # DIV q, r = self.alu.divmod_(num, v, 2 * size) if q >= (1 << size): raise CPUError("#DE overflow") else: # IDIV bits2 = 2 * size snum = num - (1 << bits2) if num >> (bits2 - 1) else num sden = sx8(v) if size == 8 else sx16(v) if size == 16 else sx32(v) q0, r0 = self.alu.divmod_(abs(snum), abs(sden), bits2) q = -q0 if (snum < 0) != (sden < 0) else q0 r = -r0 if snum < 0 else r0 if not (-(1 << (size - 1)) <= q < (1 << (size - 1))): raise CPUError("#DE overflow") q &= (1 << size) - 1; r &= (1 << size) - 1 if size == 8: self.set_r(EAX, q, 8); self.set_r(4, r, 8) # AH else: self.set_r(EAX, q, size); self.set_r(EDX, r, size) # ---------------- string ops ---------------- def string_op(self, op, S, rep): size = 8 if op in (0xA4, 0xAA, 0xA6, 0xAE, 0xAC) else S n = size // 8 step = -n if self.f[DF] else n def one(): si, di = self.r[ESI], self.r[EDI] if op in (0xA4, 0xA5): # MOVS self.wr(di, self.rd(si, n), n) self.r[ESI] = (si + step) & M32; self.r[EDI] = (di + step) & M32 elif op in (0xAA, 0xAB): # STOS self.wr(di, self.get_r(EAX, size), n) self.r[EDI] = (di + step) & M32 elif op in (0xAC, 0xAD): # LODS self.set_r(EAX, self.rd(si, n), size) self.r[ESI] = (si + step) & M32 elif op in (0xA6, 0xA7): # CMPS self.alu_op(7, self.rd(si, n), self.rd(di, n), size) self.r[ESI] = (si + step) & M32; self.r[EDI] = (di + step) & M32 else: # SCAS self.alu_op(7, self.get_r(EAX, size), self.rd(di, n), size) self.r[EDI] = (di + step) & M32 if rep is None: one(); return cmp_op = op in (0xA6, 0xA7, 0xAE, 0xAF) while self.r[ECX]: one() self.r[ECX] = (self.r[ECX] - 1) & M32 if cmp_op: if rep == "e" and not self.f[ZF]: break if rep == "ne" and self.f[ZF]: break