using Age.Engine.Model;
namespace Age.Engine.Vm;
/// One script activation: the running script, its instruction cursor, its local slots,
/// its intra-script call/ret stack, and its per-script loop-guard map. Globals live on the VM and
/// are shared across frames; everything here is per-call and discarded on return.
internal sealed class ExecFrame
{
internal readonly record struct TimedCallback(long DeadlineMs, int PrimaryOffset, int CatchUpOffset);
public readonly Script Script;
public int Pc; // entry instruction index
public readonly Frame Locals = new();
public readonly List CallStack = new(); // intra-script `call` (op 0x8f) returns
public readonly Dictionary EmitSeen = new();
public int? CoroutineYieldHandlerA; // op 0x7b: native per-frame handler PCs
public int? CoroutineYieldHandlerB;
public int? CoroutineResumePc; // op 0x199 -> handler A/B -> op 0x7c
public bool CoroutineYieldActive;
public readonly Dictionary CoroutineYieldVisits = new(); // instruction index -> visits
public readonly int[] InputCallbackTargets = Enumerable.Repeat(-1, 32).ToArray(); // op 0xfb
public int PendingInputCallbackMask; // op 0xff snapshot consumed by op 0x100
public int InputCallbackScanIndex;
public int MouseCallbackTarget = -1; // op 0xcc target dword offset
public long MouseCallbackIntervalMs;
public long MouseCallbackNextAtMs;
public readonly List TimedCallbacks = new(); // ops 0xd3/0xd4/0xd5
public int TimedCallbackCursor;
public long? TimedCallbackStartedAtMs;
public int TimedCallbackAbortOffset = -1;
public readonly HotspotRegistry Hotspots = new();
public ExecFrame(Script script, int pc) { Script = script; Pc = pc; }
}