//! A tree-walking AML interpreter — the evaluation stage on top of the parser's //! structural namespace. It executes control methods (their bodies captured by //! the parser) far enough to serve device discovery: device status (`_STA`, is a //! device present), current resource settings (`_CRS`), and the operators, control //! flow, locals/args, and //! OperationRegion field access those methods reach for. //! //! Scope: integers, buffers, strings, packages, and references; If/Else/While/ //! Return; the arithmetic/logic operators; method invocation; Name/Local/Arg //! access; CreateField buffer patching (the common current-resource-settings //! (`_CRS`) idiom); and field //! reads/writes against SystemMemory and SystemIO regions. Opcodes outside this //! set return `error.Unsupported`, which callers treat as "couldn't evaluate" and //! fall back — never a hard failure. const std = @import("std"); const opcode = @import("opcodes.zig"); const Node = @import("namespace.zig").Node; const Namespace = @import("namespace.zig").Namespace; /// Injected hardware access for OperationRegion reads/writes (the architecture VMM + pio). pub const Hal = struct { mapMmio: *const fn (physical: u64, len: u64, writable: bool) u64, pioRead: *const fn (width: u8, port: u16) u32, pioWrite: *const fn (width: u8, port: u16, value: u32) void, }; pub const Error = error{ Unsupported, Truncated, DivByZero } || std.mem.Allocator.Error; /// A runtime AML value. pub const Object = union(enum) { uninitialized, integer: u64, buffer: []u8, string: []u8, package: []Object, reference: *Node, pub fn asInteger(self: Object) Error!u64 { return switch (self) { .integer => |v| v, .buffer => |b| blk: { var v: u64 = 0; for (b, 0..) |byte, i| { if (i >= 8) break; v |= @as(u64, byte) << @intCast(i * 8); } break :blk v; }, else => error.Unsupported, }; } }; const maximum_segments = 16; const NamePath = struct { rooted: bool = false, parents: u8 = 0, segments: [maximum_segments][4]u8 = undefined, count: usize = 0, fn slice(self: *const NamePath) []const [4]u8 { return self.segments[0..self.count]; } }; const Cursor = struct { b: []const u8, i: usize = 0, fn eof(self: *Cursor) bool { return self.i >= self.b.len; } fn peek(self: *Cursor) ?u8 { return if (self.eof()) null else self.b[self.i]; } fn byte(self: *Cursor) Error!u8 { if (self.eof()) return error.Truncated; const v = self.b[self.i]; self.i += 1; return v; } fn take(self: *Cursor, n: usize) Error![]const u8 { if (self.i + n > self.b.len) return error.Truncated; const s = self.b[self.i .. self.i + n]; self.i += n; return s; } fn packageLength(self: *Cursor) Error!usize { const lead = try self.byte(); const follow: usize = lead >> 6; if (follow == 0) return lead & 0x3F; var value: usize = lead & 0x0F; var k: usize = 0; while (k < follow) : (k += 1) value |= @as(usize, try self.byte()) << @intCast(4 + k * 8); return value; } fn nameString(self: *Cursor) Error!NamePath { var name_path = NamePath{}; if (self.peek() == opcode.root_char) { name_path.rooted = true; self.i += 1; } else { while (self.peek() == opcode.parent_prefix_char) : (self.i += 1) name_path.parents += 1; } const lead = self.peek() orelse return name_path; switch (lead) { 0x00 => self.i += 1, opcode.dual_name_prefix => { self.i += 1; try self.segment(&name_path); try self.segment(&name_path); }, opcode.multi_name_prefix => { self.i += 1; const count = try self.byte(); var k: usize = 0; while (k < count) : (k += 1) try self.segment(&name_path); }, else => try self.segment(&name_path), } return name_path; } fn segment(self: *Cursor, name_path: *NamePath) Error!void { const s = try self.take(4); if (name_path.count < maximum_segments) { name_path.segments[name_path.count] = s[0..4].*; name_path.count += 1; } } }; const Frame = struct { args: [7]Object = .{.uninitialized} ** 7, locals: [8]Object = .{.uninitialized} ** 8, scope: *Node, ret: Object = .uninitialized, returned: bool = false, broke: bool = false, }; /// A CreateField binding: a name that indexes into a buffer object. const BufferField = struct { buffer: *Node, byte_off: usize, bit_width: u32 }; /// One Notify(device, code) the interpreter executed. pub const NotifyEvent = struct { node: *Node, code: u64 }; pub const Interpreter = struct { namespace: *Namespace, hal: Hal, arena: std.mem.Allocator, /// Runtime object overrides for Name nodes (Store targets, patched buffers). dynamic_overrides: std.AutoHashMapUnmanaged(*Node, Object) = .{}, /// CreateField bindings active for the current evaluation. fields: std.AutoHashMapUnmanaged(*Node, BufferField) = .{}, /// Notify(device, code) operations the last evaluation executed — a GPE or /// EC handler tells the OS "look at this device" this way. Bounded; the /// caller drains it with `takeNotifications` after `evaluate` (M21). notify_queue: [16]NotifyEvent = undefined, notify_count: usize = 0, pub fn init(namespace: *Namespace, hal: Hal, arena: std.mem.Allocator) Interpreter { return .{ .namespace = namespace, .hal = hal, .arena = arena }; } /// Evaluate a namespace object: invoke a Method, read a Name's value, or read a /// Field. Resets per-evaluation runtime state first. pub fn evaluate(self: *Interpreter, node: *Node, args: []const Object) Error!Object { self.notify_count = 0; self.dynamic_overrides.clearRetainingCapacity(); self.fields.clearRetainingCapacity(); return self.invoke(node, args); } fn invoke(self: *Interpreter, node: *Node, args: []const Object) Error!Object { switch (node.kind) { .method => { var frame = Frame{ .scope = node }; for (args, 0..) |a, i| { if (i < frame.args.len) frame.args[i] = a; } var current = Cursor{ .b = node.value }; try self.executeList(¤t, &frame); return frame.ret; }, .name => { if (self.dynamic_overrides.get(node)) |o| return o; var current = Cursor{ .b = node.value }; var frame = Frame{ .scope = node.parent orelse self.namespace.root }; return self.term(¤t, &frame); }, .field => return .{ .integer = try self.readField(node) }, else => return .{ .reference = node }, } } /// Execute a TermList until it ends or the frame returns/breaks. fn executeList(self: *Interpreter, current: *Cursor, frame: *Frame) Error!void { while (!current.eof() and !frame.returned and !frame.broke) { _ = try self.term(current, frame); } } /// Evaluate/execute one term, returning its value (`.uninitialized` for pure /// statements). fn term(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const lead = current.peek() orelse return error.Truncated; if (isNameStart(lead)) return self.nameReference(current, frame); _ = try current.byte(); return switch (lead) { opcode.zero_opcode => Object{ .integer = 0 }, opcode.one_opcode => Object{ .integer = 1 }, opcode.ones_opcode => Object{ .integer = ~@as(u64, 0) }, opcode.byte_prefix => Object{ .integer = try self.readConstant(current, 1) }, opcode.word_prefix => Object{ .integer = try self.readConstant(current, 2) }, opcode.dword_prefix => Object{ .integer = try self.readConstant(current, 4) }, opcode.qword_prefix => Object{ .integer = try self.readConstant(current, 8) }, opcode.string_prefix => try self.readString(current), opcode.buffer_opcode => try self.buffer(current, frame), opcode.package_opcode, opcode.var_package_opcode => try self.package(current, frame, lead == opcode.var_package_opcode), opcode.local0_opcode...opcode.local7_opcode => frame.locals[lead - opcode.local0_opcode], opcode.arg0_opcode...opcode.arg6_opcode => frame.args[lead - opcode.arg0_opcode], opcode.return_opcode => blk: { frame.ret = try self.term(current, frame); frame.returned = true; break :blk .uninitialized; }, opcode.break_opcode => blk: { frame.broke = true; break :blk .uninitialized; }, opcode.continue_opcode, opcode.noop_opcode => .uninitialized, opcode.if_opcode => try self.ifElse(current, frame), opcode.while_opcode => try self.whileLoop(current, frame), opcode.store_opcode => try self.store(current, frame), opcode.increment_opcode => try self.incDec(current, frame, 1), opcode.decrement_opcode => try self.incDec(current, frame, -1), opcode.add_opcode => try self.binary(current, frame, .add), opcode.subtract_opcode => try self.binary(current, frame, .sub), opcode.multiply_opcode => try self.binary(current, frame, .mul), opcode.mod_opcode => try self.binary(current, frame, .mod), opcode.and_opcode => try self.binary(current, frame, .band), opcode.or_opcode => try self.binary(current, frame, .bor), opcode.xor_opcode => try self.binary(current, frame, .bxor), opcode.nand_opcode => try self.binary(current, frame, .nand), opcode.nor_opcode => try self.binary(current, frame, .nor), opcode.shift_left_opcode => try self.binary(current, frame, .shl), opcode.shift_right_opcode => try self.binary(current, frame, .shr), opcode.divide_opcode => try self.divide(current, frame), opcode.land_opcode => try self.logic2(current, frame, .land), opcode.lor_opcode => try self.logic2(current, frame, .lor), opcode.lequal_opcode => try self.logic2(current, frame, .eq), opcode.lgreater_opcode => try self.logic2(current, frame, .gt), opcode.lless_opcode => try self.logic2(current, frame, .lt), opcode.lnot_opcode => try self.lnot(current, frame), opcode.not_opcode => blk: { const v = try self.evaluateInteger(current, frame); const r = ~v; try self.storeTarget(current, frame, .{ .integer = r }); break :blk .{ .integer = r }; }, opcode.size_of_opcode => try self.sizeOf(current, frame), opcode.index_opcode => try self.index(current, frame), opcode.dereference_of_opcode => try self.dereferenceOf(current, frame), opcode.to_integer_opcode => blk: { const v = try self.evaluateInteger(current, frame); try self.storeTarget(current, frame, .{ .integer = v }); break :blk .{ .integer = v }; }, opcode.to_buffer_opcode => try self.passThroughUnary(current, frame), opcode.notify_opcode => try self.notify(current, frame), opcode.extended_opcode_prefix => try self.ext(current, frame), // CreateXField: source, index, name (bit widths differ by op) opcode.create_bit_field_opcode => try self.createField(current, frame, 1), opcode.create_byte_field_opcode => try self.createField(current, frame, 8), opcode.create_word_field_opcode => try self.createField(current, frame, 16), opcode.create_dword_field_opcode => try self.createField(current, frame, 32), opcode.create_qword_field_opcode => try self.createField(current, frame, 64), else => error.Unsupported, }; } // --- name references ---------------------------------------------------- fn nameReference(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const name_path = try current.nameString(); const node = self.namespace.resolve(frame.scope, name_path.rooted, name_path.parents, name_path.slice()) orelse return .uninitialized; // unknown name -> treat as uninitialised switch (node.kind) { .method => { var argbuf: [7]Object = undefined; var i: usize = 0; while (i < node.arg_count and i < argbuf.len) : (i += 1) argbuf[i] = try self.term(current, frame); return self.invoke(node, argbuf[0..@min(node.arg_count, argbuf.len)]); }, .field => return .{ .integer = try self.readField(node) }, .name => return self.invoke(node, &.{}), else => return .{ .reference = node }, } } // --- data objects ------------------------------------------------------- fn readConstant(self: *Interpreter, current: *Cursor, n: usize) Error!u64 { _ = self; const bytes = try current.take(n); var v: u64 = 0; for (bytes, 0..) |b, i| v |= @as(u64, b) << @intCast(i * 8); return v; } fn readString(self: *Interpreter, current: *Cursor) Error!Object { const start = current.i; while (current.peek()) |c| { current.i += 1; if (c == 0) break; } const raw = current.b[start .. current.i - 1]; const s = try self.arena.dupe(u8, raw); return .{ .string = s }; } fn buffer(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const start = current.i; const len = try current.packageLength(); const end = @min(start + len, current.b.len); const size = try self.evaluateInteger(current, frame); const data = current.b[@min(current.i, end)..end]; const bytes = try self.arena.alloc(u8, @intCast(size)); @memset(bytes, 0); @memcpy(bytes[0..@min(bytes.len, data.len)], data[0..@min(bytes.len, data.len)]); current.i = end; return .{ .buffer = bytes }; } fn package(self: *Interpreter, current: *Cursor, frame: *Frame, variable: bool) Error!Object { const start = current.i; const len = try current.packageLength(); const end = @min(start + len, current.b.len); const count: usize = if (variable) @intCast(try self.evaluateInteger(current, frame)) else try current.byte(); const elems = try self.arena.alloc(Object, count); var i: usize = 0; while (i < count and current.i < end) : (i += 1) elems[i] = try self.term(current, frame); while (i < count) : (i += 1) elems[i] = .uninitialized; current.i = end; return .{ .package = elems }; } // --- operators ---------------------------------------------------------- const BinaryOperation = enum { add, sub, mul, mod, band, bor, bxor, nand, nor, shl, shr }; fn binary(self: *Interpreter, current: *Cursor, frame: *Frame, kind: BinaryOperation) Error!Object { const a = try self.evaluateInteger(current, frame); const b = try self.evaluateInteger(current, frame); const r: u64 = switch (kind) { .add => a +% b, .sub => a -% b, .mul => a *% b, .mod => if (b == 0) return error.DivByZero else a % b, .band => a & b, .bor => a | b, .bxor => a ^ b, .nand => ~(a & b), .nor => ~(a | b), .shl => if (b >= 64) 0 else a << @intCast(b), .shr => if (b >= 64) 0 else a >> @intCast(b), }; try self.storeTarget(current, frame, .{ .integer = r }); return .{ .integer = r }; } fn divide(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const a = try self.evaluateInteger(current, frame); const b = try self.evaluateInteger(current, frame); if (b == 0) return error.DivByZero; try self.storeTarget(current, frame, .{ .integer = a % b }); // remainder target try self.storeTarget(current, frame, .{ .integer = a / b }); // quotient target return .{ .integer = a / b }; } const LogicOperation = enum { land, lor, eq, gt, lt }; fn logic2(self: *Interpreter, current: *Cursor, frame: *Frame, kind: LogicOperation) Error!Object { const a = try self.evaluateInteger(current, frame); const b = try self.evaluateInteger(current, frame); const r = switch (kind) { .land => a != 0 and b != 0, .lor => a != 0 or b != 0, .eq => a == b, .gt => a > b, .lt => a < b, }; return .{ .integer = if (r) ~@as(u64, 0) else 0 }; } fn lnot(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { // 0x92 0x93/94/95 are the compound comparisons. const b = current.peek() orelse return error.Truncated; switch (b) { opcode.lnot.not_equal => { current.i += 1; const x = try self.evaluateInteger(current, frame); const y = try self.evaluateInteger(current, frame); return .{ .integer = if (x != y) ~@as(u64, 0) else 0 }; }, opcode.lnot.less_equal => { current.i += 1; const x = try self.evaluateInteger(current, frame); const y = try self.evaluateInteger(current, frame); return .{ .integer = if (x <= y) ~@as(u64, 0) else 0 }; }, opcode.lnot.greater_equal => { current.i += 1; const x = try self.evaluateInteger(current, frame); const y = try self.evaluateInteger(current, frame); return .{ .integer = if (x >= y) ~@as(u64, 0) else 0 }; }, else => { const x = try self.evaluateInteger(current, frame); return .{ .integer = if (x == 0) ~@as(u64, 0) else 0 }; }, } } fn incDec(self: *Interpreter, current: *Cursor, frame: *Frame, delta: i64) Error!Object { // Operand is a SuperName that is both read and written. const save = current.i; const current_value = try self.term(current, frame); const v = try current_value.asInteger(); const r = if (delta > 0) v +% 1 else v -% 1; var tcur = Cursor{ .b = current.b, .i = save }; try self.storeInto(&tcur, frame, .{ .integer = r }); return .{ .integer = r }; } fn sizeOf(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const o = try self.term(current, frame); return .{ .integer = switch (o) { .buffer => |b| b.len, .string => |s| s.len, .package => |p| p.len, else => 0, } }; } fn passThroughUnary(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const o = try self.term(current, frame); try self.storeTarget(current, frame, o); return o; } fn index(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const source = try self.term(current, frame); const element_index: usize = @intCast(try self.evaluateInteger(current, frame)); // Optional target (a reference); we don't materialise references, so store // the indexed value if a target is present. const value: Object = switch (source) { .buffer => |b| .{ .integer = if (element_index < b.len) b[element_index] else 0 }, .package => |p| if (element_index < p.len) p[element_index] else .uninitialized, .string => |s| .{ .integer = if (element_index < s.len) s[element_index] else 0 }, else => .uninitialized, }; try self.storeTarget(current, frame, value); return value; } fn dereferenceOf(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const o = try self.term(current, frame); return switch (o) { .reference => |n| self.invoke(n, &.{}), else => o, }; } // --- control flow ------------------------------------------------------- fn ifElse(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const start = current.i; const end = @min(start + try current.packageLength(), current.b.len); const cond = try self.evaluateInteger(current, frame); if (cond != 0) { var body = Cursor{ .b = current.b[0..end], .i = current.i }; try self.executeList(&body, frame); current.i = end; // Skip a trailing Else. if (current.peek() == opcode.else_opcode) { current.i += 1; const es = current.i; const ee = @min(es + try current.packageLength(), current.b.len); current.i = ee; } } else { current.i = end; if (current.peek() == opcode.else_opcode) { current.i += 1; const es = current.i; const ee = @min(es + try current.packageLength(), current.b.len); var body = Cursor{ .b = current.b[0..ee], .i = current.i }; try self.executeList(&body, frame); current.i = ee; } } return .uninitialized; } fn whileLoop(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const start = current.i; const end = @min(start + try current.packageLength(), current.b.len); const pred_at = current.i; var guard: usize = 0; while (guard < 100_000) : (guard += 1) { var pc = Cursor{ .b = current.b[0..end], .i = pred_at }; const cond = try self.evaluateInteger(&pc, frame); if (cond == 0) break; var body = Cursor{ .b = current.b[0..end], .i = pc.i }; try self.executeList(&body, frame); if (frame.returned) break; if (frame.broke) { frame.broke = false; break; } } current.i = end; return .uninitialized; } // --- store -------------------------------------------------------------- fn store(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const value = try self.term(current, frame); try self.storeInto(current, frame, value); return value; } /// A Store *target* that may be NullName (no store). fn storeTarget(self: *Interpreter, current: *Cursor, frame: *Frame, value: Object) Error!void { if (current.peek() == 0x00) { current.i += 1; // NullName return; } try self.storeInto(current, frame, value); } /// Notify(SuperName, NotifyValue): resolve the named device, evaluate the /// code, and record the pair for the caller to dispatch. AML control flow /// continues (Notify returns nothing). fn notify(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const lead = current.peek() orelse return error.Truncated; var target: ?*Node = null; if (isNameStart(lead)) { const name_path = try current.nameString(); target = self.namespace.resolve(frame.scope, name_path.rooted, name_path.parents, name_path.slice()); } else { // A non-name SuperName (Local/Arg holding a reference). const obj = try self.term(current, frame); if (obj == .reference) target = obj.reference; } const code = try self.evaluateInteger(current, frame); if (target) |node| { if (self.notify_count < self.notify_queue.len) { self.notify_queue[self.notify_count] = .{ .node = node, .code = code }; self.notify_count += 1; } } return .uninitialized; } /// The Notify events the last `evaluate` produced. Valid until the next /// `evaluate` clears the queue. pub fn takeNotifications(self: *Interpreter) []const NotifyEvent { return self.notify_queue[0..self.notify_count]; } fn storeInto(self: *Interpreter, current: *Cursor, frame: *Frame, value: Object) Error!void { const lead = current.peek() orelse return error.Truncated; if (isNameStart(lead)) { const name_path = try current.nameString(); const node = self.namespace.resolve(frame.scope, name_path.rooted, name_path.parents, name_path.slice()) orelse return; if (self.fields.get(node)) |buffer_field| { try self.writeBufferField(buffer_field, try value.asInteger()); } else if (node.kind == .field) { try self.writeField(node, try value.asInteger()); } else { try self.dynamic_overrides.put(self.arena, node, value); } return; } _ = try current.byte(); switch (lead) { 0x00 => {}, // NullName opcode.local0_opcode...opcode.local7_opcode => frame.locals[lead - opcode.local0_opcode] = value, opcode.arg0_opcode...opcode.arg6_opcode => frame.args[lead - opcode.arg0_opcode] = value, opcode.index_opcode => { const source = try self.term(current, frame); const element_index: usize = @intCast(try self.evaluateInteger(current, frame)); switch (source) { .buffer => |b| if (element_index < b.len) { b[element_index] = @truncate(try value.asInteger()); }, .package => |p| if (element_index < p.len) { p[element_index] = value; }, else => {}, } }, else => return error.Unsupported, } } // --- CreateField (buffer patching) -------------------------------------- fn createField(self: *Interpreter, current: *Cursor, frame: *Frame, bit_width: u32) Error!Object { const source = try self.term(current, frame); // source buffer (as a reference or value) const bit_index = try self.evaluateInteger(current, frame); const name_path = try current.nameString(); const node = self.namespace.resolve(frame.scope, name_path.rooted, name_path.parents, name_path.slice()) orelse return .uninitialized; // Bind the new name to the source buffer's node so stores land in it. const buffer_node: *Node = switch (source) { .reference => |n| n, else => return .uninitialized, }; // Materialise the buffer into `dynamic_overrides` so patches persist and are returned. if (self.dynamic_overrides.get(buffer_node) == null) { const value = try self.invoke(buffer_node, &.{}); try self.dynamic_overrides.put(self.arena, buffer_node, value); } const byte_off: usize = @intCast(bit_index / 8); try self.fields.put(self.arena, node, .{ .buffer = buffer_node, .byte_off = byte_off, .bit_width = bit_width }); return .uninitialized; } fn writeBufferField(self: *Interpreter, buffer_field: BufferField, value: u64) Error!void { const obj = self.dynamic_overrides.get(buffer_field.buffer) orelse return; const bytes = switch (obj) { .buffer => |b| b, else => return, }; const byte_count = (buffer_field.bit_width + 7) / 8; var k: usize = 0; while (k < byte_count and buffer_field.byte_off + k < bytes.len) : (k += 1) { bytes[buffer_field.byte_off + k] = @truncate(value >> @intCast(k * 8)); } } // --- OperationRegion field access --------------------------------------- fn readField(self: *Interpreter, field: *Node) Error!u64 { const region = field.region orelse return error.Unsupported; if (field.bit_width == 0 or field.bit_width > 64) return error.Unsupported; const base = try self.regionBase(region); const start_byte = base + field.bit_offset / 8; const shift: u7 = @intCast(field.bit_offset % 8); const total = @as(usize, shift) + field.bit_width; const byte_count = (total + 7) / 8; var raw: u128 = 0; var k: usize = 0; while (k < byte_count) : (k += 1) { raw |= @as(u128, try self.readRegionByte(region.region_space, start_byte + k)) << @intCast(k * 8); } const masked = (raw >> shift) & bitMask(field.bit_width); return @truncate(masked); } fn writeField(self: *Interpreter, field: *Node, value: u64) Error!void { const region = field.region orelse return error.Unsupported; if (field.bit_width == 0 or field.bit_width > 64) return error.Unsupported; const base = try self.regionBase(region); const start_byte = base + field.bit_offset / 8; const shift: u7 = @intCast(field.bit_offset % 8); const total = @as(usize, shift) + field.bit_width; const byte_count = (total + 7) / 8; // Read-modify-write byte by byte. var raw: u128 = 0; var k: usize = 0; while (k < byte_count) : (k += 1) { raw |= @as(u128, try self.readRegionByte(region.region_space, start_byte + k)) << @intCast(k * 8); } const mask = bitMask(field.bit_width) << shift; raw = (raw & ~mask) | ((@as(u128, value) << shift) & mask); k = 0; while (k < byte_count) : (k += 1) { try self.writeRegionByte(region.region_space, start_byte + k, @truncate(raw >> @intCast(k * 8))); } } fn regionBase(self: *Interpreter, region: *Node) Error!u64 { var current = Cursor{ .b = region.region_offset_aml }; var frame = Frame{ .scope = region.parent orelse self.namespace.root }; return (try self.term(¤t, &frame)).asInteger(); } fn readRegionByte(self: *Interpreter, space: u8, address: u64) Error!u8 { switch (space) { 0 => { // SystemMemory const virtual = self.hal.mapMmio(address & ~@as(u64, 0xFFF), 0x1000, true); const p: *align(1) const volatile u8 = @ptrFromInt(virtual + (address & 0xFFF)); return p.*; }, 1 => return @truncate(self.hal.pioRead(1, @intCast(address & 0xFFFF))), // SystemIO else => return error.Unsupported, } } fn writeRegionByte(self: *Interpreter, space: u8, address: u64, value: u8) Error!void { switch (space) { 0 => { const virtual = self.hal.mapMmio(address & ~@as(u64, 0xFFF), 0x1000, true); const p: *align(1) volatile u8 = @ptrFromInt(virtual + (address & 0xFFF)); p.* = value; }, 1 => self.hal.pioWrite(1, @intCast(address & 0xFFFF), value), else => return error.Unsupported, } } // --- extended opcodes --------------------------------------------------- fn ext(self: *Interpreter, current: *Cursor, frame: *Frame) Error!Object { const e = try current.byte(); switch (e) { opcode.extended.debug => return .uninitialized, opcode.extended.revision => return .{ .integer = 2 }, opcode.extended.timer => return .{ .integer = 0 }, // Mutex/Event ops are no-ops in this single-threaded evaluator. opcode.extended.acquire => { _ = try self.term(current, frame); // mutex SuperName _ = try current.take(2); // timeout return .{ .integer = 0 }; // acquired }, opcode.extended.release, opcode.extended.reset, opcode.extended.signal => { _ = try self.term(current, frame); return .uninitialized; }, opcode.extended.wait => { _ = try self.term(current, frame); _ = try self.term(current, frame); return .{ .integer = 0 }; }, opcode.extended.sleep, opcode.extended.stall => { _ = try self.term(current, frame); return .uninitialized; }, else => return error.Unsupported, } } fn evaluateInteger(self: *Interpreter, current: *Cursor, frame: *Frame) Error!u64 { return (try self.term(current, frame)).asInteger(); } }; fn bitMask(width: u32) u128 { if (width >= 128) return ~@as(u128, 0); return (@as(u128, 1) << @intCast(width)) - 1; } fn isNameStart(b: u8) bool { return (b >= opcode.name_char_start and b <= opcode.name_char_end) or b == opcode.name_char_underscore or b == opcode.root_char or b == opcode.parent_prefix_char or b == opcode.dual_name_prefix or b == opcode.multi_name_prefix; }