Emulation on x64 and ARM64: Recreating Machines, Not Just Running Programs

Emulation is the construction in software of one computer system’s observable behavior on another. It is why a present-day laptop can run a 1980s game console, inspect a Macintosh disk, or reproduce a workstation application whose original hardware is scarce. The useful word is behavior. A credible emulator may need to reproduce a processor, memory map, display controller, storage device, sound generator, firmware calls, timing relationships, and even some faults. It is not simply a fast program that recognizes an old executable.

Four related ideas

TechniqueWhat is retainedTypical example
EmulationA different machine’s hardware-visible behaviorA console emulator implementing its CPU, video and audio chips
VirtualizationThe same or closely related CPU architecture, isolated by a hypervisorAn x86-64 guest operating system on an x86-64 server
Compatibility layerAn application-facing operating-system or API contractA Windows API implementation translating calls for another host OS
SimulationA model sufficient for an experiment, not necessarily every externally visible detailA CPU timing model used for architecture research

The boundaries can overlap. A virtual machine monitor can emulate devices while allowing the guest’s native CPU instructions to execute directly. A compatibility layer may interpret an old binary and emulate missing calls. Simulation is appropriate when studying cache policies without needing a commercial game to boot. Preservation work normally asks a stricter question: would historically significant software encounter the same documented interfaces and relevant quirks?

From instructions to behavior

The simplest CPU emulator fetches a guest instruction, decodes it, changes a guest register and memory state, and repeats. This interpretation is clear and easy to instrument. A 6502 ADC, for example, can be expressed as host code that reads the guest accumulator, applies the guest’s carry and decimal-mode rules, updates flags, and advances the guest program counter. It is not necessarily fast: each guest instruction may invoke a large host dispatch loop.

Dynamic recompilation, often called a just-in-time compiler or JIT, translates frequently executed guest basic blocks into host machine code and caches the result. A Game Boy block can become a sequence of AArch64 instructions; an old x86 block can become x86-64 code with bookkeeping around it. JITs can be dramatically faster, but they must invalidate translated code when a guest writes to executable memory, preserve precise exceptions, and return to the emulator at interrupts or memory-mapped I/O. “Fast” translation that mishandles self-modifying DOS software is not compatibility.

Static recompilation translates ahead of time, when a known program can be analysed, while high-level emulation replaces a well-understood subsystem with a higher-level equivalent. Both can be excellent engineering choices, but neither automatically preserves every timing edge. An interpreter is therefore still valuable as a reference implementation and debugger, even when a JIT is the normal performance path.

x86-64, x64, and AArch64

x86-64 is the AMD-designed 64-bit extension of the x86 instruction-set architecture, later adopted by Intel. Microsoft popularized the label x64; in ordinary host descriptions it means x86-64, not a separate ISA. Modern desktops and servers commonly use x86-64, while many laptops, phones, development boards, and cloud instances use the 64-bit ARM architecture. ARM’s architectural name is AArch64; operating systems and vendors commonly call it ARM64. Those terms likewise describe the same 64-bit execution state in most practical discussions.

Different ISAs matter even when both have 64-bit general-purpose registers. x86-64 is a variable-length, historically layered ISA with condition flags and instructions that can combine memory access and arithmetic. AArch64 has fixed 32-bit instruction encodings, a more regular register model, and generally load/store arithmetic. A translator must reproduce the guest’s registers, flags, privilege rules, exceptions, atomic operations, and undefined or implementation-specific corners before it may choose efficient host instructions. It cannot assume an instruction with a similar English name has the same overflow, rounding, alignment, or ordering semantics.

Endianness is another boundary. x86-64 is little-endian. AArch64 is normally deployed little-endian but was designed with more byte-order flexibility; many retro machines and file formats use big-endian fields. Guest memory should be represented as guest bytes, with explicit helpers for 16-, 32-, and 64-bit loads and stores. Casting a host buffer to a native integer pointer can quietly reverse a big-endian guest field or introduce alignment faults. Network packet headers, Motorola 68000 software, disk metadata, and framebuffer pixels all make this visible.

Accuracy includes time and devices

Instruction results alone do not make a machine. A device may assert an interrupt between instructions, DMA may contend for memory, a video chip may expose a raster position, and a floppy controller may depend on bit-cell timing. Cycle-accurate emulation schedules activity at individual clock-cycle granularity. It can be necessary for software that races a beam, relies on a CPU/video bus conflict, or operates a protection scheme. It is expensive and should not be treated as a universal badge of quality: an application that uses only documented system calls may need accurate APIs and storage far more than sub-cycle audio behavior.

Most systems use event-driven scheduling. The CPU runs until the next timer, display, audio, or I/O event; devices advance their own state; then pending interrupts are delivered in a defined order. Audio must be buffered without allowing host scheduling jitter to alter guest time. Display refresh on a variable-rate host should not make a fixed-rate guest’s clock run faster. These choices explain why an emulator can show a game’s title screen correctly yet fail during music, serial transfer, or a disk loader.

Firmware is part of this contract. A PC BIOS, UEFI firmware, console boot ROM, disk-drive ROM, or Macintosh Toolbox ROM initializes hardware and supplies services expected by software. Reimplementing an interface can be legitimate and useful, but a replacement does not make a proprietary firmware image freely distributable. An emulator should state whether it includes an original-free implementation, requires a separately obtained image, or models a particular firmware revision. Version, checksum, region, and configuration are preservation metadata, not trivia.

Images are evidence with different fidelity

Image kindUsually recordsMay omit
Raw disk or cartridge dumpAddressable sectors or ROM bytesFlux transitions, weak bits, physical labels
Filesystem archiveFiles and often timestampsBoot layout, deleted data, copy protection
Flux/track captureLower-level magnetic timingInterpretation without drive parameters
Snapshot/save stateLive RAM, registers, device statePortability across emulator versions

A .img, .iso, .d64, .prg, or ROM file name tells little by itself; extensions are conventions, not proof of origin or completeness. For preservation, retain the original medium where lawful, make a write-protected working copy, calculate documented checksums, and record the acquisition source, hardware, tool version, format, region, and known defects. A convenient file extraction can be useful access copy while a lower-level capture remains the archival master.

Performance, determinism, and debugging

Host performance is not a license to skip design. JIT compilation, vectorized pixel conversion, multithreaded rendering, and host GPU APIs can reduce cost, but shared mutable state can make results dependent on thread timing. Deterministic emulation gives the same guest inputs, firmware, images, configuration, and initial state the same result. It supports regression tests, reproducible bug reports, tool-assisted recordings, and trustworthy preservation. Real-time clocks, random-number sources, network packets, host filesystem ordering, and asynchronous audio are common nondeterministic inputs; an emulator can record, virtualize, or explicitly expose them.

Debug facilities turn an emulator into a research instrument. Breakpoints, watchpoints, instruction traces, memory and register views, frame stepping, device logs, and save states help distinguish an application defect from a mistaken machine model. Differential testing is especially powerful: run a test ROM or diagnostic on two independent implementations, compare state at defined checkpoints, and investigate disagreement. Tests should include overflow flags, unaligned or invalid access where applicable, interrupt timing, DMA, media error paths, and reset behavior—not merely a successful boot.

Preservation is a social and legal practice

Emulation allows access, but access copies need provenance. Keep manuals, box scans, patches, regional versions, release dates, credits, and observations alongside hashes. Describe modified releases honestly; a translated, cracked, repaired, or fan-patched image is not identical to the original. The Internet Archive’s emulation and software collections and the Software Preservation Network illustrate why documentation, rights, and reproducibility matter alongside executable bytes.

Age is not public-domain status. ROMs, games, operating systems, firmware, manuals, and music can remain protected by copyright, license terms, and local anti-circumvention law. Obtain software from rights holders, licensed reissues, clearly authorized archives, public-domain or free-software projects, or dumps from media you own where local law permits. Do not treat an online download as licensed merely because it is common. This is not legal advice; archives and users should consult applicable law and the rights holder’s terms. Careful sourcing protects creators and makes an emulation collection more credible.

A useful standard

The best emulator is not necessarily the one with the largest compatibility list or the most elaborate rendering path. It is one whose intended machine, firmware, media, timing model, known limitations, and evidence are explicit. On x86-64 or ARM64, that clarity lets modern hardware serve old software without erasing the differences that made the old machine what it was.