Implement ADV layout cursor and bounds
This commit is contained in:
@@ -2227,6 +2227,45 @@ the deliberately deferred shared-profile write `0x1a2`.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### ADV layout reset cursor and overflow bounds — `0x79` / `0x1c1` (2026-07-21)
|
||||||
|
|
||||||
|
SYSTEM4 uses these two three-operand operations as a pair while constructing its nine ADV layouts. They
|
||||||
|
configure persistent layout properties used by later resets; neither operation draws text or moves the
|
||||||
|
current cursor immediately.
|
||||||
|
|
||||||
|
`op_0x79_set_adv_text_reset_cursor@0x41eb50` reads `(layout_slot, x, y)` and calls
|
||||||
|
`adv_text_layout_set_reset_cursor@0x44fed0`. Slot zero resolves through the current-layout selector at
|
||||||
|
manager `+0x4c8`; otherwise the helper selects `manager+0x414[slot]`. It writes `x/y` to layout
|
||||||
|
`+0x1c/+0x20`. This is distinct from `0x7a`, whose `adv_text_set_cursor@0x4530f0` path modifies the live
|
||||||
|
cursor record immediately.
|
||||||
|
|
||||||
|
`op_0x1c1_set_adv_text_bounds@0x41f6c0` reads `(layout_slot, right, bottom)` and calls
|
||||||
|
`adv_text_layout_set_bounds@0x44ff00`, which writes the pair to layout `+0x24/+0x28`. They are absolute
|
||||||
|
layout-local overflow coordinates. `adv_text_layout_check_overflow@0x45efc0` compares glyph end x/y
|
||||||
|
against those fields and returns horizontal/vertical overflow bits. When a layout resets,
|
||||||
|
`adv_text_layout_reset_cursor_and_bounds_record@0x455070` replaces its retained state with a five-dword
|
||||||
|
record `{0, reset_x, reset_y, right, bottom}` copied from all four configured fields.
|
||||||
|
|
||||||
|
The corpus supplies a complete consistency check: all nine sites for each opcode are in SYSTEM4 and
|
||||||
|
alternate after layouts 1–9 are defined and initially reset. Layout 1 configures cursor `(100,47)` and
|
||||||
|
bounds `(720,147)`; layouts 2–6 use `(45,42)` and `(645,135)`; layout 7 uses `(53,10)` and `(495,180)`;
|
||||||
|
layout 8 uses `(53,10)` and `(495,60)`; layout 9 uses `(10,10)` and `(250,368)`. Layout 1 computes its
|
||||||
|
bounds as `100+620` and `47+100`, further excluding width/height-delta semantics.
|
||||||
|
|
||||||
|
The port now keeps configured reset cursor and right/bottom bounds in its engine-owned layout state.
|
||||||
|
`0x70` initializes bounds from width/height, `0x79` changes only the deferred reset cursor, `0x1c1`
|
||||||
|
changes the bounds, and `0x71` restores the configured cursor while notifying the host of the concrete
|
||||||
|
selected slot. Retained snapshots carry the boundaries, and ordinary/history Godot labels are positioned
|
||||||
|
from the layout origin and sized from `right-cursor_x` / `bottom-cursor_y`; the former hardcoded slot-1
|
||||||
|
rectangle is gone. The ordinary overlay remains bound to the parent input wait's captured layout while a
|
||||||
|
nested callback such as HISTORY selects and renders into layouts 2–6; closing the callback therefore
|
||||||
|
reveals the retained parent text at its original geometry rather than consulting the callback's last
|
||||||
|
current-layout selection. The direct-scene SYSTEM4 bootstrap also recognizes all nine configuration pairs and
|
||||||
|
evaluates the two constant `add` expressions used for slot 1 without entering SYSTEM4's control flow. This
|
||||||
|
is script-owned state, with no boot seed or game-specific coordinates in the runtime.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Native walls backlog (targets for this loop)
|
## Native walls backlog (targets for this loop)
|
||||||
|
|
||||||
- ~~**call-script dispatch**~~ — **SOLVED** (above): `call-script <id>` = raw SYS4INI file index.
|
- ~~**call-script dispatch**~~ — **SOLVED** (above): `call-script <id>` = raw SYS4INI file index.
|
||||||
|
|||||||
@@ -8,11 +8,13 @@
|
|||||||
### 0x70 `define-adv-text-layout` (define-adv-text-layout, argc 5)
|
### 0x70 `define-adv-text-layout` (define-adv-text-layout, argc 5)
|
||||||
- **summary:** (layout_slot)(width)(height)(x)(y) - configure an ADV text layout/surface and, when history recording is enabled, append its logical history index and arm the next retained record as a group start.
|
- **summary:** (layout_slot)(width)(height)(x)(y) - configure an ADV text layout/surface and, when history recording is enabled, append its logical history index and arm the next retained record as a group start.
|
||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
|
- **depended on by:** 0x79, 0x1c1
|
||||||
- **evidence:** Ghidra /v2: op_0x70_handler@0x41e4e0 forwards the five operands and ctx+0x55110 to adv_text_layout_define@0x4550e0. The worker stores width/height/origin, creates or resizes surface slot layout+0x14, appends {layout_slot,current_record_count} to the 8-byte history index unless suppression bit31 is set, and arms manager+0xd40[slot] so the next 0x48-byte record receives group-start flag bit0.
|
- **evidence:** Ghidra /v2: op_0x70_handler@0x41e4e0 forwards the five operands and ctx+0x55110 to adv_text_layout_define@0x4550e0. The worker stores width/height/origin, creates or resizes surface slot layout+0x14, appends {layout_slot,current_record_count} to the 8-byte history index unless suppression bit31 is set, and arms manager+0xd40[slot] so the next 0x48-byte record receives group-start flag bit0.
|
||||||
|
|
||||||
### 0x71 `reset-adv-text-layout` (reset-adv-text-layout, argc 1)
|
### 0x71 `reset-adv-text-layout` (reset-adv-text-layout, argc 1)
|
||||||
- **summary:** (layout_slot) - clear/reset an ADV text layout, append a retained-history boundary when recording is enabled, snapshot the current code/text position, and commit pending ReadTextDB records. T1 entries target these structural reset sites.
|
- **summary:** (layout_slot) - clear/reset an ADV text layout, append a retained-history boundary when recording is enabled, snapshot the current code/text position, and commit pending ReadTextDB records. T1 entries target these structural reset sites.
|
||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
|
- **depended on by:** 0x79, 0x1c1
|
||||||
- **evidence:** Corpus: T1 targets op-0x71 records, but the operand is a layout slot (SC0000 uses 1; HISTORY computes 2..6), not an anchor id. Ghidra /v2: op_0x71_handler@0x41e540 calls adv_text_layout_reset@0x455210 with ctx+0x55110; the worker clears the selected layout and appends {slot,current_record_count}/arms group-start unless suppressed. The handler also snapshots (frame_pc-frame_codebase)/4 and text state and calls read_text_db_commit_pending@0x46ae20.
|
- **evidence:** Corpus: T1 targets op-0x71 records, but the operand is a layout slot (SC0000 uses 1; HISTORY computes 2..6), not an anchor id. Ghidra /v2: op_0x71_handler@0x41e540 calls adv_text_layout_reset@0x455210 with ctx+0x55110; the worker clears the selected layout and appends {slot,current_record_count}/arms group-start unless suppressed. The handler also snapshots (frame_pc-frame_codebase)/4 and text state and calls read_text_db_commit_pending@0x46ae20.
|
||||||
|
|
||||||
### 0x72 `wait-for-input` (wait-for-input, argc 1)
|
### 0x72 `wait-for-input` (wait-for-input, argc 1)
|
||||||
@@ -45,6 +47,12 @@
|
|||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
- **evidence:** Ghidra /v2: op_0x78_handler@0x41eb10 writes operand 1 to text-manager+0x558 and rebuilds font state. Direct draw and text_history_render_records branch on this field (including a distinct mode-3 offset path). HISTORY.BIN selects mode 3 and restores mode 3 with the normal ADV font preset.
|
- **evidence:** Ghidra /v2: op_0x78_handler@0x41eb10 writes operand 1 to text-manager+0x558 and rebuilds font state. Direct draw and text_history_render_records branch on this field (including a distinct mode-3 offset path). HISTORY.BIN selects mode 3 and restores mode 3 with the normal ADV font preset.
|
||||||
|
|
||||||
|
### 0x79 `set-adv-text-reset-cursor` (set-adv-text-reset-cursor, argc 3)
|
||||||
|
- **summary:** (layout_slot)(x)(y) - configure the cursor coordinates restored by a later reset of the selected ADV text layout. Slot 0 selects the current layout. This does not move the live cursor immediately; opcode 0x7a does that.
|
||||||
|
- **grounding:** source=investigation, confidence=high
|
||||||
|
- **depends on:** 0x70, 0x71
|
||||||
|
- **evidence:** Ghidra /v2: op_0x79_set_adv_text_reset_cursor@0x41eb50 reads three operands and calls adv_text_layout_set_reset_cursor@0x44fed0. Slot 0 resolves through the manager current-layout field at +0x4c8; the selected layout pointer is manager+0x414[slot], and x/y are stored at layout +0x1c/+0x20. adv_text_layout_reset_cursor_and_bounds_record@0x455070 later copies those configured values into the initial retained record on layout reset. SYSTEM4 has all nine corpus sites, paired with 0x1c1 after defining/resetting layouts 1..9; slot 1 configures (100,47), layouts 2..6 configure (45,42), layouts 7/8 (53,10), and layout 9 (10,10). Port implementation stores the configured cursor separately from the live cursor, restores it on 0x71, and replays the SYSTEM4 configuration in the direct-scene bootstrap.
|
||||||
|
|
||||||
### 0x7a `set-adv-text-cursor` (set-adv-text-cursor, argc 3)
|
### 0x7a `set-adv-text-cursor` (set-adv-text-cursor, argc 3)
|
||||||
- **summary:** (layout_slot)(x)(y) - set the cursor in the selected ADV text layout's last 20-byte record. Slot 0 selects the current layout.
|
- **summary:** (layout_slot)(x)(y) - set the cursor in the selected ADV text layout's last 20-byte record. Slot 0 selects the current layout.
|
||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
@@ -80,6 +88,12 @@
|
|||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
- **evidence:** Ghidra /v2: op_0x1bb_handler@0x41f650 writes 0x80000000 to ctx+0x55110 for operand 0 and zero for operand 1, rejecting other values. Text/layout/metadata/voice paths test or propagate that high bit before appending retained history. HISTORY.BIN disables recording at entry and reenables it at exit so the backlog UI does not record itself.
|
- **evidence:** Ghidra /v2: op_0x1bb_handler@0x41f650 writes 0x80000000 to ctx+0x55110 for operand 0 and zero for operand 1, rejecting other values. Text/layout/metadata/voice paths test or propagate that high bit before appending retained history. HISTORY.BIN disables recording at entry and reenables it at exit so the backlog UI does not record itself.
|
||||||
|
|
||||||
|
### 0x1c1 `set-adv-text-bounds` (set-adv-text-bounds, argc 3)
|
||||||
|
- **summary:** (layout_slot)(right)(bottom) - configure the selected ADV text layout's right and bottom overflow boundaries in layout-local coordinates. Slot 0 selects the current layout.
|
||||||
|
- **grounding:** source=investigation, confidence=high
|
||||||
|
- **depends on:** 0x70, 0x71
|
||||||
|
- **evidence:** Ghidra /v2: op_0x1c1_set_adv_text_bounds@0x41f6c0 reads three operands and calls adv_text_layout_set_bounds@0x44ff00. That helper stores right/bottom at layout +0x24/+0x28; adv_text_layout_reset_cursor_and_bounds_record@0x455070 copies them into the initial retained record, and adv_text_layout_check_overflow@0x45efc0 compares glyph end coordinates against them. SYSTEM4 has all nine sites, paired with 0x79. Slot 1 computes (720,147), layouts 2..6 use (645,135), layout 7 (495,180), layout 8 (495,60), and layout 9 (250,368), proving these are layout-local boundary coordinates rather than width/height deltas. Port implementation retains the configured bounds in engine-owned layout snapshots and sizes ordinary/history Godot labels from right-cursor_x and bottom-cursor_y.
|
||||||
|
|
||||||
### 0x1ce `set-adv-wait-indicator-enabled` (u0041B9F0, argc 1)
|
### 0x1ce `set-adv-wait-indicator-enabled` (u0041B9F0, argc 1)
|
||||||
- **summary:** (enabled) - explicitly start or stop the animated ADV input-wait indicator service.
|
- **summary:** (enabled) - explicitly start or stop the animated ADV input-wait indicator service.
|
||||||
- **grounding:** source=investigation, confidence=high
|
- **grounding:** source=investigation, confidence=high
|
||||||
@@ -931,10 +945,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
|||||||
- **summary:** —
|
- **summary:** —
|
||||||
- **grounding:** source=kelebek, confidence=med
|
- **grounding:** source=kelebek, confidence=med
|
||||||
|
|
||||||
### 0x79 `u0041AD30` (u0041AD30, argc 3)
|
|
||||||
- **summary:** —
|
|
||||||
- **grounding:** source=kelebek, confidence=low
|
|
||||||
|
|
||||||
### 0x7f `u00414C60` (u00414C60, argc 1)
|
### 0x7f `u00414C60` (u00414C60, argc 1)
|
||||||
- **summary:** —
|
- **summary:** —
|
||||||
- **grounding:** source=kelebek, confidence=low
|
- **grounding:** source=kelebek, confidence=low
|
||||||
@@ -1151,10 +1161,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
|
|||||||
- **summary:** —
|
- **summary:** —
|
||||||
- **grounding:** source=kelebek, confidence=low
|
- **grounding:** source=kelebek, confidence=low
|
||||||
|
|
||||||
### 0x1c1 `u0041B820` (u0041B820, argc 3)
|
|
||||||
- **summary:** —
|
|
||||||
- **grounding:** source=kelebek, confidence=low
|
|
||||||
|
|
||||||
### 0x1c8 `toString` (toString, argc 2)
|
### 0x1c8 `toString` (toString, argc 2)
|
||||||
- **summary:** —
|
- **summary:** —
|
||||||
- **grounding:** source=kelebek, confidence=med
|
- **grounding:** source=kelebek, confidence=med
|
||||||
|
|||||||
@@ -280,8 +280,12 @@ GAMESTART profile operations still deliberately deferred. The reached `0x63`/`0x
|
|||||||
also implemented: native `0x63` aliases a typed backing-cell address into a pointer, while `0x1b0` copies a
|
also implemented: native `0x63` aliases a typed backing-cell address into a pointer, while `0x1b0` copies a
|
||||||
counted dword span through direct or pointer endpoints. The traced natural boot reaches both without
|
counted dword span through direct or pointer endpoints. The traced natural boot reaches both without
|
||||||
fallback; UNITECH is now 31/31 handled and CALCCC 14/15, with only deferred profile op `0x1a2` remaining.
|
fallback; UNITECH is now 31/31 handled and CALCCC 14/15, with only deferred profile op `0x1a2` remaining.
|
||||||
The next reached effectful cluster to investigate is SYSTEM4's paired `0x79`/`0x1c1` setup. Closing Godot
|
SYSTEM4's paired `0x79`/`0x1c1` setup is now natively resolved and implemented: `0x79` configures the
|
||||||
currently releases a parked ADV wait before process teardown, so the page map may contain one trailing
|
cursor restored by later layout resets, while `0x1c1` configures layout-local right/bottom overflow
|
||||||
|
boundaries. `AdvTextHistory`, VM dispatch, the direct-scene SYSTEM4 bootstrap, and ordinary/history Godot
|
||||||
|
label geometry now share that script-owned state; the former slot-1 hardcoding is removed. The next
|
||||||
|
natural Game Start diagnostic should identify the next reached effectful fallback. Closing Godot currently
|
||||||
|
releases a parked ADV wait before process teardown, so the page map may contain one trailing
|
||||||
shutdown-only page; the final timeline `input-wait` is the authoritative stop.
|
shutdown-only page; the final timeline `input-wait` is the authoritative stop.
|
||||||
|
|
||||||
## Stage B2 — Faithful full boot
|
## Stage B2 — Faithful full boot
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class AdvTextHistoryTests
|
|||||||
Assert.Equal(123, metadata.Value);
|
Assert.Equal(123, metadata.Value);
|
||||||
Assert.Equal(1, metadata.AuxValue);
|
Assert.Equal(1, metadata.AuxValue);
|
||||||
Assert.Equal((24, 0xf0e0d0L), (metadata.Style.PrimaryFontSize, metadata.Style.TextColor));
|
Assert.Equal((24, 0xf0e0d0L), (metadata.Style.PrimaryFontSize, metadata.Style.TextColor));
|
||||||
Assert.Equal(new AdvTextLayoutSnapshot(1, 640, 160, 80, 430, 12, 34), metadata.Layout);
|
Assert.Equal(new AdvTextLayoutSnapshot(1, 640, 160, 80, 430, 12, 34, 640, 160), metadata.Layout);
|
||||||
},
|
},
|
||||||
voice =>
|
voice =>
|
||||||
{
|
{
|
||||||
@@ -109,7 +109,27 @@ public class AdvTextHistoryTests
|
|||||||
var record = Assert.Single(history.Records);
|
var record = Assert.Single(history.Records);
|
||||||
Assert.Equal("new", record.Text);
|
Assert.Equal("new", record.Text);
|
||||||
Assert.False(record.Flags.HasFlag(AdvTextHistoryRecordFlags.GroupStart));
|
Assert.False(record.Flags.HasFlag(AdvTextHistoryRecordFlags.GroupStart));
|
||||||
Assert.Equal(new AdvTextLayoutSnapshot(3, 320, 90, 20, 400, 0, 0), record.Layout);
|
Assert.Equal(new AdvTextLayoutSnapshot(3, 320, 90, 20, 400, 0, 0, 320, 90), record.Layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConfiguredCursorIsDeferredUntilResetWhileBoundsApplyToTheLayout()
|
||||||
|
{
|
||||||
|
var history = new AdvTextHistory();
|
||||||
|
history.DefineLayout(1, 800, 160, 0, 430);
|
||||||
|
history.ResetLayout(1);
|
||||||
|
history.SetCursor(1, 12, 34);
|
||||||
|
|
||||||
|
history.SetResetCursor(1, 100, 47);
|
||||||
|
history.SetBounds(1, 720, 147);
|
||||||
|
|
||||||
|
Assert.Equal(new AdvTextLayoutSnapshot(1, 800, 160, 0, 430, 12, 34, 720, 147),
|
||||||
|
history.GetLayoutSnapshot(1));
|
||||||
|
|
||||||
|
history.ResetLayout(1);
|
||||||
|
|
||||||
|
Assert.Equal(new AdvTextLayoutSnapshot(1, 800, 160, 0, 430, 100, 47, 720, 147),
|
||||||
|
history.GetLayoutSnapshot(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -31,6 +31,58 @@ public class AdvTextOpsTests
|
|||||||
Assert.Equal((13, 1, 1, "speaker"), Assert.Single(host.SurfaceStrings));
|
Assert.Equal((13, 1, 1, "speaker"), Assert.Single(host.SurfaceStrings));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LayoutResetRestoresConfiguredCursorAndPreservesConfiguredBounds()
|
||||||
|
{
|
||||||
|
var table = OpcodeTableJson.Load(Paths.OpcodesJson);
|
||||||
|
var script = ScriptAssembler.Assemble(table, "ADV_LAYOUT_CONFIG",
|
||||||
|
new List<(int, Operand[])>
|
||||||
|
{
|
||||||
|
(0x70, new[] { new Operand(0, 1), new Operand(0, 800), new Operand(0, 160),
|
||||||
|
new Operand(0, 0), new Operand(0, 430) }),
|
||||||
|
(0x71, new[] { new Operand(0, 1) }),
|
||||||
|
(0x79, new[] { new Operand(0, 1), new Operand(0, 100), new Operand(0, 47) }),
|
||||||
|
(0x1c1, new[] { new Operand(0, 1), new Operand(0, 720), new Operand(0, 147) }),
|
||||||
|
(0x7a, new[] { new Operand(0, 1), new Operand(0, 12), new Operand(0, 34) }),
|
||||||
|
(0x71, new[] { new Operand(0, 1) }),
|
||||||
|
(0x2, Array.Empty<Operand>()),
|
||||||
|
}, Array.Empty<string>());
|
||||||
|
var host = new RecordingHost();
|
||||||
|
var vm = new VirtualMachine(script, table, host);
|
||||||
|
|
||||||
|
vm.Run();
|
||||||
|
|
||||||
|
Assert.Equal("exit", vm.HaltReason);
|
||||||
|
Assert.Equal(new[] { (1, 0, 0), (1, 12, 34), (1, 100, 47) }, host.TextCursors);
|
||||||
|
Assert.Equal(new AdvTextLayoutSnapshot(1, 800, 160, 0, 430, 100, 47, 720, 147),
|
||||||
|
vm.TextHistory.GetLayoutSnapshot(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void System4BootstrapReplaysAllResetCursorAndBoundsConfigurations()
|
||||||
|
{
|
||||||
|
var table = OpcodeTableJson.Load(Paths.OpcodesJson);
|
||||||
|
var scripts = Sys4ScriptProvider.Load(table);
|
||||||
|
var history = new AdvTextHistory();
|
||||||
|
|
||||||
|
Assert.Equal(9, AdvTextLayoutBootstrap.ApplyLeadingDefinitionsAndResets(
|
||||||
|
scripts.RequireByName("SYSTEM4.BIN"), table, history));
|
||||||
|
|
||||||
|
// SYSTEM4's configuration follows its initial reset run, so the configured cursor is deferred.
|
||||||
|
Assert.Equal((0, 0, 720, 147), CursorAndBounds(history.GetLayoutSnapshot(1)));
|
||||||
|
history.ResetLayout(1);
|
||||||
|
Assert.Equal((100, 47, 720, 147), CursorAndBounds(history.GetLayoutSnapshot(1)));
|
||||||
|
history.ResetLayout(4);
|
||||||
|
Assert.Equal((45, 42, 645, 135), CursorAndBounds(history.GetLayoutSnapshot(4)));
|
||||||
|
history.ResetLayout(8);
|
||||||
|
Assert.Equal((53, 10, 495, 60), CursorAndBounds(history.GetLayoutSnapshot(8)));
|
||||||
|
history.ResetLayout(9);
|
||||||
|
Assert.Equal((10, 10, 250, 368), CursorAndBounds(history.GetLayoutSnapshot(9)));
|
||||||
|
|
||||||
|
static (int X, int Y, int Right, int Bottom) CursorAndBounds(AdvTextLayoutSnapshot layout)
|
||||||
|
=> (layout.CursorX, layout.CursorY, layout.Right, layout.Bottom);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void WaitIndicatorConfigurationReachesHost()
|
public void WaitIndicatorConfigurationReachesHost()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class HistoryPresentationOpsTests
|
|||||||
var render = Assert.Single(host.HistoryRenders);
|
var render = Assert.Single(host.HistoryRenders);
|
||||||
Assert.Equal((4, 0, "retained dialogue"),
|
Assert.Equal((4, 0, "retained dialogue"),
|
||||||
(render.LayoutSlot, render.FirstRecordIndex, render.Text));
|
(render.LayoutSlot, render.FirstRecordIndex, render.Text));
|
||||||
Assert.Equal(new AdvTextLayoutSnapshot(4, 600, 150, 65, 150, 45, 42), render.Layout);
|
Assert.Equal(new AdvTextLayoutSnapshot(4, 600, 150, 65, 150, 45, 42, 600, 150), render.Layout);
|
||||||
Assert.Equal((24, 0xffffffL, 0x606060L, 8),
|
Assert.Equal((24, 0xffffffL, 0x606060L, 8),
|
||||||
(render.Style.PrimaryFontSize, render.Style.TextColor, render.Style.EffectColor,
|
(render.Style.PrimaryFontSize, render.Style.TextColor, render.Style.EffectColor,
|
||||||
render.Style.LineSpacing));
|
render.Style.LineSpacing));
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ public readonly record struct AdvTextLayoutSnapshot(
|
|||||||
int OriginX,
|
int OriginX,
|
||||||
int OriginY,
|
int OriginY,
|
||||||
int CursorX,
|
int CursorX,
|
||||||
int CursorY);
|
int CursorY,
|
||||||
|
int Right,
|
||||||
|
int Bottom);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// One semantic counterpart of AGE's 0x48-byte retained text record. Metadata uses
|
/// One semantic counterpart of AGE's 0x48-byte retained text record. Metadata uses
|
||||||
@@ -80,8 +82,12 @@ public sealed class AdvTextHistory
|
|||||||
public int Height;
|
public int Height;
|
||||||
public int OriginX;
|
public int OriginX;
|
||||||
public int OriginY;
|
public int OriginY;
|
||||||
|
public int ResetCursorX;
|
||||||
|
public int ResetCursorY;
|
||||||
public int CursorX;
|
public int CursorX;
|
||||||
public int CursorY;
|
public int CursorY;
|
||||||
|
public int Right;
|
||||||
|
public int Bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<AdvTextHistoryRecord> _records = new();
|
private readonly List<AdvTextHistoryRecord> _records = new();
|
||||||
@@ -105,6 +111,8 @@ public sealed class AdvTextHistory
|
|||||||
layout.Height = height;
|
layout.Height = height;
|
||||||
layout.OriginX = originX;
|
layout.OriginX = originX;
|
||||||
layout.OriginY = originY;
|
layout.OriginY = originY;
|
||||||
|
layout.Right = width;
|
||||||
|
layout.Bottom = height;
|
||||||
AppendBoundary(slot);
|
AppendBoundary(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,11 +120,27 @@ public sealed class AdvTextHistory
|
|||||||
{
|
{
|
||||||
int slot = SelectLayout(requestedSlot);
|
int slot = SelectLayout(requestedSlot);
|
||||||
var layout = GetOrCreateLayout(slot);
|
var layout = GetOrCreateLayout(slot);
|
||||||
layout.CursorX = 0;
|
layout.CursorX = layout.ResetCursorX;
|
||||||
layout.CursorY = 0;
|
layout.CursorY = layout.ResetCursorY;
|
||||||
AppendBoundary(slot);
|
AppendBoundary(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetResetCursor(int requestedSlot, int x, int y)
|
||||||
|
{
|
||||||
|
int slot = ResolveLayout(requestedSlot);
|
||||||
|
var layout = GetOrCreateLayout(slot);
|
||||||
|
layout.ResetCursorX = x;
|
||||||
|
layout.ResetCursorY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBounds(int requestedSlot, int right, int bottom)
|
||||||
|
{
|
||||||
|
int slot = ResolveLayout(requestedSlot);
|
||||||
|
var layout = GetOrCreateLayout(slot);
|
||||||
|
layout.Right = right;
|
||||||
|
layout.Bottom = bottom;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetCursor(int requestedSlot, int x, int y)
|
public void SetCursor(int requestedSlot, int x, int y)
|
||||||
{
|
{
|
||||||
int slot = ResolveLayout(requestedSlot);
|
int slot = ResolveLayout(requestedSlot);
|
||||||
@@ -133,6 +157,9 @@ public sealed class AdvTextHistory
|
|||||||
layout.OriginY = y;
|
layout.OriginY = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AdvTextLayoutSnapshot GetLayoutSnapshot(int requestedSlot)
|
||||||
|
=> SnapshotLayout(ResolveLayout(requestedSlot));
|
||||||
|
|
||||||
public void AppendText(int requestedSlot, int sourceOffset, string text, AdvTextStyle style,
|
public void AppendText(int requestedSlot, int sourceOffset, string text, AdvTextStyle style,
|
||||||
AdvTextHistoryRecordFlags flags = AdvTextHistoryRecordFlags.None)
|
AdvTextHistoryRecordFlags flags = AdvTextHistoryRecordFlags.None)
|
||||||
{
|
{
|
||||||
@@ -291,7 +318,8 @@ public sealed class AdvTextHistory
|
|||||||
{
|
{
|
||||||
var layout = GetOrCreateLayout(slot);
|
var layout = GetOrCreateLayout(slot);
|
||||||
return new AdvTextLayoutSnapshot(slot, layout.Width, layout.Height,
|
return new AdvTextLayoutSnapshot(slot, layout.Width, layout.Height,
|
||||||
layout.OriginX, layout.OriginY, layout.CursorX, layout.CursorY);
|
layout.OriginX, layout.OriginY, layout.CursorX, layout.CursorY,
|
||||||
|
layout.Right, layout.Bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppendBoundary(int slot)
|
private void AppendBoundary(int slot)
|
||||||
@@ -325,7 +353,8 @@ public sealed class AdvTextHistory
|
|||||||
|
|
||||||
var layout = GetOrCreateLayout(slot);
|
var layout = GetOrCreateLayout(slot);
|
||||||
var snapshot = new AdvTextLayoutSnapshot(slot, layout.Width, layout.Height,
|
var snapshot = new AdvTextLayoutSnapshot(slot, layout.Width, layout.Height,
|
||||||
layout.OriginX, layout.OriginY, layout.CursorX, layout.CursorY);
|
layout.OriginX, layout.OriginY, layout.CursorX, layout.CursorY,
|
||||||
|
layout.Right, layout.Bottom);
|
||||||
_records.Add(new AdvTextHistoryRecord(kind, flags, snapshot, style, text, value, auxValue, sourceOffset));
|
_records.Add(new AdvTextHistoryRecord(kind, flags, snapshot, style, text, value, auxValue, sourceOffset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ public static class AdvTextLayoutBootstrap
|
|||||||
{
|
{
|
||||||
int? defineOpcode = table.ByLabel("define-adv-text-layout");
|
int? defineOpcode = table.ByLabel("define-adv-text-layout");
|
||||||
int? resetOpcode = table.ByLabel("reset-adv-text-layout");
|
int? resetOpcode = table.ByLabel("reset-adv-text-layout");
|
||||||
|
int? resetCursorOpcode = table.ByLabel("set-adv-text-reset-cursor");
|
||||||
|
int? boundsOpcode = table.ByLabel("set-adv-text-bounds");
|
||||||
|
int? addOpcode = table.ByLabel("add");
|
||||||
if (defineOpcode == null || resetOpcode == null) return 0;
|
if (defineOpcode == null || resetOpcode == null) return 0;
|
||||||
|
|
||||||
bool foundDefinition = false;
|
bool foundDefinition = false;
|
||||||
@@ -46,6 +49,81 @@ public static class AdvTextLayoutBootstrap
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SYSTEM4 configures the cursor restored by future resets and the right/bottom overflow bounds
|
||||||
|
// after the initial define/reset run. Most operands are immediate; slot 1 computes its bounds with
|
||||||
|
// two constant add instructions. Interpret only that small, data-only expression vocabulary rather
|
||||||
|
// than entering SYSTEM4's later menu/session flow.
|
||||||
|
if (resetCursorOpcode != null && boundsOpcode != null)
|
||||||
|
{
|
||||||
|
var localInts = new Dictionary<int, long>();
|
||||||
|
bool inLayoutBlock = false;
|
||||||
|
int cursorConfigurations = 0;
|
||||||
|
int boundConfigurations = 0;
|
||||||
|
foreach (var instruction in systemScript.Instructions)
|
||||||
|
{
|
||||||
|
if (!inLayoutBlock)
|
||||||
|
{
|
||||||
|
if (instruction.Opcode != defineOpcode.Value) continue;
|
||||||
|
inLayoutBlock = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addOpcode != null && instruction.Opcode == addOpcode.Value)
|
||||||
|
{
|
||||||
|
if (instruction.Args.Count == 3 && instruction.Args[0].Type == 9
|
||||||
|
&& TryResolveConstant(instruction.Args[1], localInts, out long left)
|
||||||
|
&& TryResolveConstant(instruction.Args[2], localInts, out long right))
|
||||||
|
localInts[checked((int)instruction.Args[0].Value)] = left + right;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instruction.Opcode == resetCursorOpcode.Value)
|
||||||
|
{
|
||||||
|
var values = ResolveConfiguration(instruction, localInts, systemScript.Name);
|
||||||
|
history.SetResetCursor(values[0], values[1], values[2]);
|
||||||
|
cursorConfigurations++;
|
||||||
|
}
|
||||||
|
else if (instruction.Opcode == boundsOpcode.Value)
|
||||||
|
{
|
||||||
|
var values = ResolveConfiguration(instruction, localInts, systemScript.Name);
|
||||||
|
history.SetBounds(values[0], values[1], values[2]);
|
||||||
|
boundConfigurations++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursorConfigurations >= definitions && boundConfigurations >= definitions) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return definitions;
|
return definitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int[] ResolveConfiguration(
|
||||||
|
Instruction instruction, IReadOnlyDictionary<int, long> localInts, string scriptName)
|
||||||
|
{
|
||||||
|
if (instruction.Args.Count != 3)
|
||||||
|
throw new InvalidDataException(
|
||||||
|
$"{scriptName}@0x{instruction.Offset:x}: ADV layout configuration must use three operands");
|
||||||
|
var values = new int[3];
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
if (!TryResolveConstant(instruction.Args[i], localInts, out long value))
|
||||||
|
throw new InvalidDataException(
|
||||||
|
$"{scriptName}@0x{instruction.Offset:x}: ADV layout configuration operand {i + 1} " +
|
||||||
|
"is not an immediate or constant local integer");
|
||||||
|
values[i] = checked((int)value);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryResolveConstant(
|
||||||
|
Operand operand, IReadOnlyDictionary<int, long> localInts, out long value)
|
||||||
|
{
|
||||||
|
if (operand.Type == 0)
|
||||||
|
{
|
||||||
|
value = operand.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (operand.Type == 9 && localInts.TryGetValue(checked((int)operand.Value), out value)) return true;
|
||||||
|
value = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -827,12 +827,23 @@ public sealed class VirtualMachine
|
|||||||
(int)Read(a[3]), (int)Read(a[4]));
|
(int)Read(a[3]), (int)Read(a[4]));
|
||||||
return pc + 1;
|
return pc + 1;
|
||||||
case "reset-adv-text-layout": // 0x71: reset layout and begin the next logical retained group
|
case "reset-adv-text-layout": // 0x71: reset layout and begin the next logical retained group
|
||||||
TextHistory.ResetLayout((int)Read(a[0]));
|
{
|
||||||
_host.ClearRenderedAdvTextLayout((int)Read(a[0]));
|
int requestedSlot = (int)Read(a[0]);
|
||||||
|
TextHistory.ResetLayout(requestedSlot);
|
||||||
|
var layout = TextHistory.GetLayoutSnapshot(requestedSlot);
|
||||||
|
_host.SetAdvTextCursor(layout.Slot, layout.CursorX, layout.CursorY);
|
||||||
|
_host.ClearRenderedAdvTextLayout(layout.Slot);
|
||||||
|
return pc + 1;
|
||||||
|
}
|
||||||
|
case "set-adv-text-reset-cursor": // 0x79: configure cursor restored by a later 0x71
|
||||||
|
TextHistory.SetResetCursor((int)Read(a[0]), (int)Read(a[1]), (int)Read(a[2]));
|
||||||
return pc + 1;
|
return pc + 1;
|
||||||
case "set-adv-text-cursor": // 0x7a (layout slot, x, y); slot 0 means current natively
|
case "set-adv-text-cursor": // 0x7a (layout slot, x, y); slot 0 means current natively
|
||||||
TextHistory.SetCursor((int)Read(a[0]), (int)Read(a[1]), (int)Read(a[2]));
|
TextHistory.SetCursor((int)Read(a[0]), (int)Read(a[1]), (int)Read(a[2]));
|
||||||
_host.SetAdvTextCursor((int)Read(a[0]), (int)Read(a[1]), (int)Read(a[2])); return pc + 1;
|
_host.SetAdvTextCursor((int)Read(a[0]), (int)Read(a[1]), (int)Read(a[2])); return pc + 1;
|
||||||
|
case "set-adv-text-bounds": // 0x1c1: layout-local right/bottom overflow boundaries
|
||||||
|
TextHistory.SetBounds((int)Read(a[0]), (int)Read(a[1]), (int)Read(a[2]));
|
||||||
|
return pc + 1;
|
||||||
case "configure-adv-wait-indicator": // 0x73: per-layout animated input-wait marker
|
case "configure-adv-wait-indicator": // 0x73: per-layout animated input-wait marker
|
||||||
_host.ConfigureAdvWaitIndicator(new AdvWaitIndicatorConfig(
|
_host.ConfigureAdvWaitIndicator(new AdvWaitIndicatorConfig(
|
||||||
(int)Read(a[0]), (int)Read(a[1]), (int)Read(a[2]), (int)Read(a[3]),
|
(int)Read(a[0]), (int)Read(a[1]), (int)Read(a[2]), (int)Read(a[3]),
|
||||||
|
|||||||
@@ -159,6 +159,20 @@ public sealed class GodotAdvHost : IHost
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Layout that owns the ordinary ADV overlay. Nested callback scripts such as HISTORY can select and
|
||||||
|
/// mutate other layouts while the parent wait remains parked; those transient selections must not move
|
||||||
|
/// the parent page when its overlay becomes visible again.
|
||||||
|
/// </summary>
|
||||||
|
public int AdvPageLayoutSlot
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_textLock)
|
||||||
|
return IsWaiting && _activeWaitLayout != 0 ? _activeWaitLayout : _currentAdvLayout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IReadOnlyList<SurfaceTextDraw> SnapshotSurfaceText(int surfaceSlot)
|
public IReadOnlyList<SurfaceTextDraw> SnapshotSurfaceText(int surfaceSlot)
|
||||||
{
|
{
|
||||||
lock (_textLock)
|
lock (_textLock)
|
||||||
|
|||||||
@@ -714,8 +714,10 @@ public partial class Main : Godot.Control
|
|||||||
_text.Visible = !_host.IsAdvPagePresentationSuspended && !_vm.IsRawInputCallbackActive;
|
_text.Visible = !_host.IsAdvPagePresentationSuspended && !_vm.IsRawInputCallbackActive;
|
||||||
if (!_text.Visible) return;
|
if (!_text.Visible) return;
|
||||||
var t = _host.SnapshotAdvText();
|
var t = _host.SnapshotAdvText();
|
||||||
_text.Position = new Vector2(t.X, 430 + t.Y);
|
var layout = _vm.TextHistory.GetLayoutSnapshot(_host.AdvPageLayoutSlot);
|
||||||
_text.Size = new Vector2(System.Math.Max(1, 720 - t.X), System.Math.Max(1, 147 - t.Y));
|
_text.Position = new Vector2(layout.OriginX + layout.CursorX, layout.OriginY + layout.CursorY);
|
||||||
|
_text.Size = new Vector2(System.Math.Max(1, layout.Right - layout.CursorX),
|
||||||
|
System.Math.Max(1, layout.Bottom - layout.CursorY));
|
||||||
int count = System.Math.Clamp(t.VisibleGlyphs, 0, t.Text.Length);
|
int count = System.Math.Clamp(t.VisibleGlyphs, 0, t.Text.Length);
|
||||||
_text.Text = count == 0 ? "" : t.Text[..count];
|
_text.Text = count == 0 ? "" : t.Text[..count];
|
||||||
}
|
}
|
||||||
@@ -733,8 +735,8 @@ public partial class Main : Godot.Control
|
|||||||
}
|
}
|
||||||
var layout = batch.Layout;
|
var layout = batch.Layout;
|
||||||
label.Position = new Vector2(layout.OriginX + layout.CursorX, layout.OriginY + layout.CursorY);
|
label.Position = new Vector2(layout.OriginX + layout.CursorX, layout.OriginY + layout.CursorY);
|
||||||
label.Size = new Vector2(System.Math.Max(1, layout.Width - layout.CursorX),
|
label.Size = new Vector2(System.Math.Max(1, layout.Right - layout.CursorX),
|
||||||
System.Math.Max(1, layout.Height - layout.CursorY));
|
System.Math.Max(1, layout.Bottom - layout.CursorY));
|
||||||
label.Text = batch.Text;
|
label.Text = batch.Text;
|
||||||
ApplyAdvTextStyle(label, batch.Style);
|
ApplyAdvTextStyle(label, batch.Style);
|
||||||
label.Visible = true;
|
label.Visible = true;
|
||||||
|
|||||||
@@ -1182,33 +1182,33 @@ observed_types = ["imm"]
|
|||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
op = 0x79
|
op = 0x79
|
||||||
label = "u0041AD30"
|
label = "set-adv-text-reset-cursor"
|
||||||
argc = 3
|
argc = 3
|
||||||
abi_source = "kelebek+decode-validated"
|
abi_source = "kelebek+decode-validated"
|
||||||
|
|
||||||
[opcode.semantics]
|
[opcode.semantics]
|
||||||
name = "u0041AD30"
|
name = "set-adv-text-reset-cursor"
|
||||||
category = "unknown"
|
category = "adv"
|
||||||
summary = ""
|
summary = "(layout_slot)(x)(y) - configure the cursor coordinates restored by a later reset of the selected ADV text layout. Slot 0 selects the current layout. This does not move the live cursor immediately; opcode 0x7a does that."
|
||||||
noop_headless = false
|
noop_headless = false
|
||||||
source = "kelebek"
|
source = "investigation"
|
||||||
confidence = "low"
|
confidence = "high"
|
||||||
depends_on = []
|
depends_on = [0x70, 0x71]
|
||||||
evidence = ""
|
evidence = "Ghidra /v2: op_0x79_set_adv_text_reset_cursor@0x41eb50 reads three operands and calls adv_text_layout_set_reset_cursor@0x44fed0. Slot 0 resolves through the manager current-layout field at +0x4c8; the selected layout pointer is manager+0x414[slot], and x/y are stored at layout +0x1c/+0x20. adv_text_layout_reset_cursor_and_bounds_record@0x455070 later copies those configured values into the initial retained record on layout reset. SYSTEM4 has all nine corpus sites, paired with 0x1c1 after defining/resetting layouts 1..9; slot 1 configures (100,47), layouts 2..6 configure (45,42), layouts 7/8 (53,10), and layout 9 (10,10). Port implementation stores the configured cursor separately from the live cursor, restores it on 0x71, and replays the SYSTEM4 configuration in the direct-scene bootstrap."
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 1
|
i = 1
|
||||||
role = ""
|
role = "layout slot (0 = current)"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 2
|
i = 2
|
||||||
role = ""
|
role = "reset cursor x"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 3
|
i = 3
|
||||||
role = ""
|
role = "reset cursor y"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
@@ -4092,33 +4092,33 @@ evidence = ""
|
|||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
op = 0x1c1
|
op = 0x1c1
|
||||||
label = "u0041B820"
|
label = "set-adv-text-bounds"
|
||||||
argc = 3
|
argc = 3
|
||||||
abi_source = "kelebek+decode-validated"
|
abi_source = "kelebek+decode-validated"
|
||||||
|
|
||||||
[opcode.semantics]
|
[opcode.semantics]
|
||||||
name = "u0041B820"
|
name = "set-adv-text-bounds"
|
||||||
category = "unknown"
|
category = "adv"
|
||||||
summary = ""
|
summary = "(layout_slot)(right)(bottom) - configure the selected ADV text layout's right and bottom overflow boundaries in layout-local coordinates. Slot 0 selects the current layout."
|
||||||
noop_headless = false
|
noop_headless = false
|
||||||
source = "kelebek"
|
source = "investigation"
|
||||||
confidence = "low"
|
confidence = "high"
|
||||||
depends_on = []
|
depends_on = [0x70, 0x71]
|
||||||
evidence = ""
|
evidence = "Ghidra /v2: op_0x1c1_set_adv_text_bounds@0x41f6c0 reads three operands and calls adv_text_layout_set_bounds@0x44ff00. That helper stores right/bottom at layout +0x24/+0x28; adv_text_layout_reset_cursor_and_bounds_record@0x455070 copies them into the initial retained record, and adv_text_layout_check_overflow@0x45efc0 compares glyph end coordinates against them. SYSTEM4 has all nine sites, paired with 0x79. Slot 1 computes (720,147), layouts 2..6 use (645,135), layout 7 (495,180), layout 8 (495,60), and layout 9 (250,368), proving these are layout-local boundary coordinates rather than width/height deltas. Port implementation retains the configured bounds in engine-owned layout snapshots and sizes ordinary/history Godot labels from right-cursor_x and bottom-cursor_y."
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 1
|
i = 1
|
||||||
role = ""
|
role = "layout slot (0 = current)"
|
||||||
observed_types = ["imm"]
|
observed_types = ["imm"]
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 2
|
i = 2
|
||||||
role = ""
|
role = "right overflow boundary"
|
||||||
observed_types = ["imm", "l-int"]
|
observed_types = ["imm", "l-int"]
|
||||||
|
|
||||||
[[opcode.semantics.args]]
|
[[opcode.semantics.args]]
|
||||||
i = 3
|
i = 3
|
||||||
role = ""
|
role = "bottom overflow boundary"
|
||||||
observed_types = ["imm", "l-int"]
|
observed_types = ["imm", "l-int"]
|
||||||
|
|
||||||
[[opcode]]
|
[[opcode]]
|
||||||
|
|||||||
Reference in New Issue
Block a user