diff options
Diffstat (limited to 'src/builtins.zig')
| -rw-r--r-- | src/builtins.zig | 101 |
1 files changed, 96 insertions, 5 deletions
diff --git a/src/builtins.zig b/src/builtins.zig index 5a6b6ea9..0c9993a7 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -28,15 +28,21 @@ const output_pane = @import("output_pane.zig"); const image_pane = @import("image_pane.zig"); const config = @import("config.zig"); const runtime_config = @import("runtime_config.zig"); +const board_memory = @import("board_memory.zig"); /// The platform's runtime-setting facilities, stated once as plain data. /// Registry generation, leader paths, Config, and EffectCode all consume this /// exact value rather than rebuilding equivalent-looking boolean expressions. pub const capabilities: runtime_config.Capabilities = .{ .font_picker = pardes.font_picker, - .panel_transitions = pardes.platform != .web, + // A transition is composited by the shell, and EffectCode has to be able + // to show WHICH compositor: only the three hosted shells are in this + // package, so the hostless platforms have no honest source to print. + .panel_transitions = pardes.hosted, .scene_shaders = pardes.platform == .gui or pardes.platform == .macos, - .tagline_font_size = pardes.platform != .tty, + // The tty's font belongs to its emulator, and the P4 firmware's belongs to + // whatever terminal is on the other end of the serial line. + .tagline_font_size = pardes.platform != .tty and pardes.platform != .p4, }; /// What a builtin gets to act on. One bundle rather than five parameters @@ -150,7 +156,12 @@ pub const OutputTraits = struct { /// on the builtin enum being constructed. pub const registry = struct { pub fn Builtin() type { - @setEvalBranchQuota(20_000); + // The duplicate-name check below is O(n^2) string comparisons over every manual builtin AND + // every generated setting, so this quota grows quadratically with the builtin count. 20,000 + // was enough until three more (Peek/Poke/Hexdump) tipped `-Dplatform=gui` over with + // "evaluation exceeded 20000 backwards branches". Raised with room rather than to the next + // value that happens to pass, so the next builtin does not have to rediscover this. + @setEvalBranchQuota(200_000); const manual = manualBuiltinList(); const generated = settingList(); const count = manual.len + generated.len; @@ -198,6 +209,18 @@ test "capabilities exactly gate setting and effect-source builtins" { try std.testing.expectEqual(EffectCode.enabled, effect_code_registered); } +// The three memory words, checked the same way but at COMPTIME rather than in +// a test, because the property is about builds this test binary is not: the +// tty suite can only ever observe its own platform, and what matters is that +// `-Dplatform=web -Dtarget=wasm32-freestanding` does not quietly hand a +// browser tab a Poke. Every build of every platform now proves its own half. +comptime { + for ([_][]const u8{ "Peek", "Poke", "Hexdump" }) |name| + if (@hasField(registry.Builtin(), name) != board_memory.enabled) @compileError( + "bare-metal memory word gating leaked: " ++ name, + ); +} + // ---- the two acme verbs ---- // Look and Execute are the verbs the whole environment is built on, and they @@ -336,7 +359,7 @@ pub const ThemeSel = struct { /// core owns parsing and keeps the last valid value across a bad live edit. pub const ThemeFile = struct { pub const takes_arg = true; - pub const enabled = pardes.platform != .web; + pub const enabled = pardes.hosted; pub fn run(c: Ctx) void { if (comptime enabled) c.p.requestThemeFile(c.id, c.arg orelse return) @@ -349,7 +372,7 @@ pub const ThemeFile = struct { /// `<config>/themes/builtin`. Filesystem work remains a host effect, just like /// Dump and Save; the build-time ring itself is the sole source of the data. pub const DumpThemes = struct { - pub const enabled = pardes.platform != .web; + pub const enabled = pardes.hosted; pub fn run(c: Ctx) void { if (comptime enabled) { if (c.p.opts.config_dir == null) { @@ -894,3 +917,71 @@ pub const Lspwhy = struct { c.p.lspRequest(c.id, .explain, ""); } }; + +// ---- the machine's address space (bare metal only) ---- +// +// Three words gated by `board_memory.enabled`, which is a fact about the +// TARGET (freestanding, and not wasm) rather than about `pardes.platform` — +// see the reasoning there. Today that is exactly `-Dplatform=p4`; what makes +// it the right predicate is that a second bare-metal port gets them without +// anyone remembering to add an enum arm, and the browser never does. +// Elsewhere they are absent from the command enum, the help index, the leader +// table and the dispatcher, which is the gate ThemeFile and DumpThemes +// already use. +// +// They are not a debugger and not a privilege: with no OS there is no MMU, no +// supervisor and no process, so pardes IS the system software and all 2^32 +// addresses are already its own. RAM, the peripheral registers behind the +// console it is talking to you over, and its own .text are one flat space, and +// a word that could reach only part of it would be pretending to be an +// application. What you actually reach for these for is the case a hosted +// editor never has: the display did not come up, and the question is whether +// the register you thought you wrote holds what you thought you wrote. +// +// Implementation, parsing, the volatile accesses and the clamp are all in +// board_memory.zig, the way the PDF words live in pdf_pane.zig — these three +// structs are the words, their argument contract, and where the answer goes. + +/// `Peek <addr> [count]` — count 32-bit words (default 1) as `addr: value` +/// rows, hex or decimal address, refused rather than trapped when unaligned. +pub const Peek = struct { + pub const takes_arg = true; + pub const enabled = board_memory.enabled; + pub const output: OutputTraits = .{ .name = config.peek_buffer }; + pub fn run(c: Ctx) void { + if (comptime enabled) apply(c) else unreachable; + } + fn apply(c: Ctx) void { + board_memory.peek(c.p, c.id, c.arg orelse "") catch |err| + c.p.reportError(c.id, "peek", err); + } +}; + +/// `Poke <addr> <value>` — one 32-bit store, answered on the message row with +/// the value written AND the value that reads back, which on MMIO is the +/// interesting half (see board_memory.poke). +pub const Poke = struct { + pub const takes_arg = true; + pub const enabled = board_memory.enabled; + pub fn run(c: Ctx) void { + if (comptime enabled) apply(c) else unreachable; + } + fn apply(c: Ctx) void { + board_memory.poke(c.p, c.id, c.arg orelse "") catch |err| + c.p.reportError(c.id, "poke", err); + } +}; + +/// `Hexdump <addr> [len]` — len bytes (default 256) in `hexdump -C`'s layout. +pub const Hexdump = struct { + pub const takes_arg = true; + pub const enabled = board_memory.enabled; + pub const output: OutputTraits = .{ .name = config.hexdump_buffer }; + pub fn run(c: Ctx) void { + if (comptime enabled) apply(c) else unreachable; + } + fn apply(c: Ctx) void { + board_memory.hexdump(c.p, c.id, c.arg orelse "") catch |err| + c.p.reportError(c.id, "hexdump", err); + } +}; |
