Post-reorg cleanup: POSIX layer, and naming fixes
Follow-up to the monorepo re-org. Suite 35/35 plus host tests green. POSIX compatibility is now its own library, library/posix/ (unistd, stdio), layered strictly over the runtime — it calls the runtime's IPC/heap, never system calls directly. The runtime is now POSIX-free (the danos-native application ABI). The VFS wire protocol is danos-native throughout (Stat -> FileStatus, .stat -> .status, O_CREAT -> create); the POSIX layer maps the POSIX spellings at the boundary. The coding standard's ABI-name exception is scoped to one place: a file is allowed POSIX spellings only if it lives under library/posix/ — everywhere else, danos naming with no exception. Naming fixes, all mechanical: - initrd -> initial-ramdisk: the source file, the module, the tool (make-initial-ramdisk.py), the artifact (initial-ramdisk.img, including the bootloader's load path), and the identifiers. - system/kernel/device-service.zig -> devices-broker.zig: it is ring-0 kernel code (the trusted device table + claim capability), not a ring-3 service. The future user-space device *manager* (policy) will live in system/services/. - Dropped the daemon `d` suffix: hpetd -> hpet, busd -> bus. A driver lives in system/drivers/, so the folder already says what it is; encoding the role in the name too is redundant. The coding standard drops that exception. - system/devices/aml/interp.zig -> interpreter.zig (the type was already Interpreter).
This commit is contained in:
@@ -0,0 +1,736 @@
|
||||
//! 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 };
|
||||
|
||||
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) = .{},
|
||||
|
||||
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.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.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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user