neural-doom / x86_core.py
Quazim0t0's picture
Neural DOOM gradio app (chromatic dark)
e72b994 verified
Raw
History Blame Contribute Delete
26.2 kB
"""
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