func (r *Replay) Feed(op models.Op) {
	var ops []models.Op
	switch o := op.(type) {
	case *OpFrame:
		ops = o.Ops
	default:
		ops = []models.Op{op}
	case *OpKeyframe:
		r.Flush()
		for _, v := range o.Ops {
			r.update(v)
		}
		return
	}
	for _, op := range ops {
		switch o := op.(type) {
		case *OpJmp:
			if o.Addr != r.PC {
				r.Flush()
			}
			r.Emit(o, nil)
			r.update(o)
		case *OpStep:
			r.Flush()
			r.pending = o
		case *OpSyscall:
			r.Flush()
			r.update(o)
			r.Emit(o, o.Ops)
		default:
			r.effects = append(r.effects, op)
		}
	}
	r.Flush()
}
Feed() is the entry point handling Op structs.
It calls update() and combines side-effects with instructions
package trace
import (
	"container/list"
	"encoding/binary"
	"github.com/pkg/errors"
	"github.com/lunixbochs/usercorn/go/models"
	"github.com/lunixbochs/usercorn/go/models/cpu"
	"github.com/lunixbochs/usercorn/go/models/debug"
)
type Replay struct {
	Arch   *models.Arch
	OS     *models.OS
	Mem    *cpu.Mem
	Regs   map[int]uint64
	SpRegs map[int][]byte
	PC, SP uint64
	Callstack models.Callstack
	Debug     *debug.Debug
	Inscount  uint64
	pending   *OpStep
	effects   []models.Op
	callbacks []func(models.Op, []models.Op)
	History   *list.List
	CanRewind bool
}
func NewReplay(arch *models.Arch, os *models.OS, order binary.ByteOrder, dbg *debug.Debug) *Replay {
	return &Replay{
		Arch:    arch,
		OS:      os,
		Mem:     cpu.NewMem(uint(arch.Bits), order),
		Regs:    make(map[int]uint64),
		SpRegs:  make(map[int][]byte),
		Debug:   dbg,
		History: list.New(),
	}
}
func (r *Replay) Listen(cb func(models.Op, []models.Op)) {
	r.callbacks = append(r.callbacks, cb)
}
func (r *Replay) Rewind(by, addr uint64) error {
	if !r.CanRewind {
		return errors.New("rewind is not enabled")
	}
	return nil
}
func (r *Replay) pushMapUndo(addr, size uint64) {
	for _, mm := range r.Mem.Maps().FindRange(addr, size) {
		if addr, size, ok := mm.Intersect(addr, size); ok {
			page := mm.Slice(addr, size)
			op := &OpMemMap{Addr: page.Addr, Size: page.Size, Prot: uint8(mm.Prot), Desc: page.Desc}
			if page.File != nil {
				op.File = page.File.Name
				op.Off = page.File.Off
				op.Len = page.File.Len
			}
			r.History.PushBack(op)
			data, _ := r.Mem.MemRead(addr, size)
			r.History.PushBack(&OpMemWrite{addr, data})
		}
	}
}
func (r *Replay) pushUndo(op models.Op) {
	switch o := op.(type) {
	case *OpJmp:  
		r.History.PushBack(&OpJmp{r.PC, 0})
	case *OpStep:
		r.History.PushBack(o)
	case *OpReg:  
		r.History.PushBack(&OpReg{Num: o.Num, Val: r.Regs[int(o.Num)]})
	case *OpSpReg:
		r.History.PushBack(&OpSpReg{Num: o.Num, Val: r.SpRegs[int(o.Num)]})
	case *OpMemMap:  
		r.History.PushBack(&OpMemUnmap{o.Addr, o.Size})
		r.pushMapUndo(o.Addr, o.Size)
	case *OpMemUnmap:
		r.pushMapUndo(o.Addr, o.Size)
	case *OpMemProt:
		for _, mm := range r.Mem.Maps().FindRange(o.Addr, o.Size) {
			r.History.PushBack(&OpMemProt{mm.Addr, mm.Size, uint8(mm.Prot)})
		}
	case *OpMemWrite:
		data, _ := r.Mem.MemRead(o.Addr, uint64(len(o.Data)))
		r.History.PushBack(&OpMemWrite{o.Addr, data})
	}
}
func (r *Replay) update(op models.Op) {
	if r.CanRewind {
		r.pushUndo(op)
	}
	switch o := op.(type) {
	case *OpJmp:  
		r.PC = o.Addr
	case *OpStep:
		r.PC += uint64(o.Size)
	case *OpReg:  
		if int(o.Num) == r.Arch.SP {
			r.SP = o.Val
			r.Callstack.Update(r.PC, r.SP)
		}
		r.Regs[int(o.Num)] = o.Val
	case *OpSpReg:
		r.SpRegs[int(o.Num)] = o.Val
	case *OpMemMap:  
		page := r.Mem.Sim.Map(o.Addr, uint64(o.Size), int(o.Prot), true)
		page.Desc = o.Desc
		if o.File != "" {
			page.File = &cpu.FileDesc{Name: o.File, Off: o.Off, Len: o.Len}
		}
	case *OpMemUnmap:
		r.Mem.MemUnmap(o.Addr, uint64(o.Size))
	case *OpMemProt:
		r.Mem.MemProt(o.Addr, uint64(o.Size), int(o.Prot))
	case *OpMemWrite:
		r.Mem.MemWrite(o.Addr, o.Data)
	case *OpSyscall:
		for _, v := range o.Ops {
			r.update(v)
		}
	}
}