Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Trace File Format

The binary format the emulator writes, and the debugger and tracer read, when execution tracing is active. It’s reference material for writing an independent trace file reader (e.g. a script or alternate viewer) — the bundled emma65-tracer binary already decodes it into disassembly listings (see Running the Tracer), and the debugger’s Trace window reads it live without needing to touch this format directly.

A trace file is a fixed 8-byte header followed by a sequence of fixed-width 16-byte records, all little-endian, with no trailer.

OffsetLengthFieldDescription
04magicASCII E65T
41format versionCurrently 2. A reader must reject any other value.
51CPU variant0 = Cmos65C02, 1 = Wdc65C02
62reservedAlways zero

The CPU variant identifies which 65C02 variant produced the trace — relevant to a decoder because Wdc65C02 adds 34 opcodes (STP, WAI, BBR0–7, BBS0–7, RMB0–7, SMB0–7) beyond the base Cmos65C02 set.

Records

Each record is exactly 16 bytes:

OffsetLengthFieldDescription
08instr_idu64. See Instruction correlation below.
81tag0 = Registers, 1 = Read, 2 = Write, 3 = Cycles
97payloadTag-dependent, zero-padded

A reader determines a record’s meaning from the tag byte alone; the payload layout is fixed per tag, not length-prefixed.

Registers (tag 0)

A snapshot of all CPU registers taken immediately before the instruction identified by instr_id began executing. Emitted once per instruction, as the first record for that instr_id.

Payload offsetLengthField
01A
11X
21Y
31S
42PC (u16)
61P (status register, see below)

The P byte is the eight processor status flags packed as N V - B D I Z C (bit 7 down to bit 0), where - is the always-set UNUSED bit — the same encoding pushed to the stack on real hardware:

Bit76543210
FlagNVUNUSEDBDIZC

Read (tag 1) / Write (tag 2)

A single bus access performed while executing the instruction identified by instr_id. A record is emitted for every bus read and write the CPU performs — operand fetches, opcode fetches, stack pushes/pops, and each device access — never for a device’s side-effect-free peek.

Payload offsetLengthField
02addr (u16)
21value
34(padding)

Cycles (tag 3)

The total clock cycle count for the instruction identified by instr_id (base cycles plus any addressing-mode or branch-taken extra cycles), known only once the instruction has finished executing. Emitted once per instruction, as the last record for that instr_id — after every Registers, Read, and Write record sharing that id.

Payload offsetLengthField
01cycle count
16(padding)

Instruction correlation

instr_id is a monotonically increasing counter (wrapping on overflow, which in practice never happens) assigned once per instruction executed. Every record belonging to the same instruction — its Registers snapshot, each bus Read/Write it performs, and its final Cycles total — shares the same instr_id, in that relative order, though other instructions’ records never interleave with them since the CPU executes one instruction to completion before the next begins. A reader reconstructing a per-instruction view (as emma65-tracer does) can therefore group records by run of equal instr_id.

Seeking

Because every record is exactly 16 bytes, the byte offset of record n (0-based, counting from the first record after the header) is 8 + n * 16. A reader with a seekable source can jump directly to any record without scanning from the start — this is how the debugger’s Trace window serves windowed reads over a large live trace file.