This commit is contained in:
bQUARKz 2026-02-20 15:08:09 +00:00
parent 25d4f3eacb
commit bd507aeaa5
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
6 changed files with 390 additions and 58 deletions

View File

@ -0,0 +1,190 @@
//! Minimal deterministic assembler for the canonical disassembly format.
//!
//! This is intended primarily for roundtrip tests: `bytes -> disassemble -> assemble -> bytes`.
//! It supports all mnemonics emitted by `disassembler.rs` and their operand formats.
use crate::isa::core::CoreOpCode;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AsmError {
EmptyLine,
UnknownMnemonic(String),
UnexpectedOperand(String),
MissingOperand(String),
InvalidOperand(String),
}
fn emit_u16(v: u16, out: &mut Vec<u8>) { out.extend_from_slice(&v.to_le_bytes()); }
fn emit_u32(v: u32, out: &mut Vec<u8>) { out.extend_from_slice(&v.to_le_bytes()); }
fn emit_i32(v: i32, out: &mut Vec<u8>) { out.extend_from_slice(&v.to_le_bytes()); }
fn emit_i64(v: i64, out: &mut Vec<u8>) { out.extend_from_slice(&v.to_le_bytes()); }
fn emit_f64_bits(bits: u64, out: &mut Vec<u8>) { out.extend_from_slice(&bits.to_le_bytes()); }
fn parse_u32_any(s: &str) -> Result<u32, AsmError> {
let s = s.trim();
if let Some(rest) = s.strip_prefix("0x") { u32::from_str_radix(rest, 16).map_err(|_| AsmError::InvalidOperand(s.into())) } else { s.parse::<u32>().map_err(|_| AsmError::InvalidOperand(s.into())) }
}
fn parse_i32_any(s: &str) -> Result<i32, AsmError> {
s.trim().parse::<i32>().map_err(|_| AsmError::InvalidOperand(s.into()))
}
fn parse_i64_any(s: &str) -> Result<i64, AsmError> {
s.trim().parse::<i64>().map_err(|_| AsmError::InvalidOperand(s.into()))
}
fn parse_f64_bits(s: &str) -> Result<u64, AsmError> {
let s = s.trim();
let s = s.strip_prefix("f64:").ok_or_else(|| AsmError::InvalidOperand(s.into()))?;
let hex = s.strip_prefix("0x").ok_or_else(|| AsmError::InvalidOperand(s.into()))?;
if hex.len() != 16 { return Err(AsmError::InvalidOperand(s.into())); }
u64::from_str_radix(hex, 16).map_err(|_| AsmError::InvalidOperand(s.into()))
}
fn parse_keyvals(s: &str) -> Result<(&str, &str), AsmError> {
// Parses formats like: "fn=123, captures=2" or "fn=3, argc=1"
let mut parts = s.split(',');
let a = parts.next().ok_or_else(|| AsmError::MissingOperand(s.into()))?.trim();
let b = parts.next().ok_or_else(|| AsmError::MissingOperand(s.into()))?.trim();
if parts.next().is_some() { return Err(AsmError::InvalidOperand(s.into())); }
Ok((a, b))
}
fn parse_pair<'a>(a: &'a str, ka: &str, b: &'a str, kb: &str) -> Result<(u32,u32), AsmError> {
let (ka_l, va_s) = a.split_once('=').ok_or_else(|| AsmError::InvalidOperand(a.into()))?;
let (kb_l, vb_s) = b.split_once('=').ok_or_else(|| AsmError::InvalidOperand(b.into()))?;
if ka_l.trim() != ka || kb_l.trim() != kb { return Err(AsmError::InvalidOperand(format!("expected keys {} and {}", ka, kb))); }
let va = parse_u32_any(va_s)?;
let vb = parse_u32_any(vb_s)?;
Ok((va, vb))
}
fn parse_mnemonic(line: &str) -> (&str, &str) {
let line = line.trim();
if let Some(sp) = line.find(char::is_whitespace) {
let (mn, rest) = line.split_at(sp);
(mn, rest.trim())
} else {
(line, "")
}
}
pub fn assemble(src: &str) -> Result<Vec<u8>, AsmError> {
let mut out = Vec::new();
for raw_line in src.lines() {
let line = raw_line.trim();
if line.is_empty() { continue; }
let (mn, ops) = parse_mnemonic(line);
match mn {
// Zero-operand
"NOP" => { emit_u16(CoreOpCode::Nop as u16, &mut out); }
"HALT" => { emit_u16(CoreOpCode::Halt as u16, &mut out); }
"TRAP" => { emit_u16(CoreOpCode::Trap as u16, &mut out); }
"DUP" => { emit_u16(CoreOpCode::Dup as u16, &mut out); }
"SWAP" => { emit_u16(CoreOpCode::Swap as u16, &mut out); }
"ADD" => { emit_u16(CoreOpCode::Add as u16, &mut out); }
"SUB" => { emit_u16(CoreOpCode::Sub as u16, &mut out); }
"MUL" => { emit_u16(CoreOpCode::Mul as u16, &mut out); }
"DIV" => { emit_u16(CoreOpCode::Div as u16, &mut out); }
"MOD" => { emit_u16(CoreOpCode::Mod as u16, &mut out); }
"NEG" => { emit_u16(CoreOpCode::Neg as u16, &mut out); }
"EQ" => { emit_u16(CoreOpCode::Eq as u16, &mut out); }
"NEQ" => { emit_u16(CoreOpCode::Neq as u16, &mut out); }
"LT" => { emit_u16(CoreOpCode::Lt as u16, &mut out); }
"LTE" => { emit_u16(CoreOpCode::Lte as u16, &mut out); }
"GT" => { emit_u16(CoreOpCode::Gt as u16, &mut out); }
"GTE" => { emit_u16(CoreOpCode::Gte as u16, &mut out); }
"AND" => { emit_u16(CoreOpCode::And as u16, &mut out); }
"OR" => { emit_u16(CoreOpCode::Or as u16, &mut out); }
"NOT" => { emit_u16(CoreOpCode::Not as u16, &mut out); }
"BIT_AND" => { emit_u16(CoreOpCode::BitAnd as u16, &mut out); }
"BIT_OR" => { emit_u16(CoreOpCode::BitOr as u16, &mut out); }
"BIT_XOR" => { emit_u16(CoreOpCode::BitXor as u16, &mut out); }
"SHL" => { emit_u16(CoreOpCode::Shl as u16, &mut out); }
"SHR" => { emit_u16(CoreOpCode::Shr as u16, &mut out); }
"RET" => { emit_u16(CoreOpCode::Ret as u16, &mut out); }
"YIELD" => { emit_u16(CoreOpCode::Yield as u16, &mut out); }
"FRAME_SYNC" => { emit_u16(CoreOpCode::FrameSync as u16, &mut out); }
// One u32 immediate (decimal or hex accepted for SYSCALL only; others decimal ok)
"JMP" => {
if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::Jmp as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"JMP_IF_FALSE" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::JmpIfFalse as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"JMP_IF_TRUE" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::JmpIfTrue as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"PUSH_CONST" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::PushConst as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"PUSH_I64" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::PushI64 as u16, &mut out); emit_i64(parse_i64_any(ops)?, &mut out);
}
"PUSH_F64" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::PushF64 as u16, &mut out); emit_f64_bits(parse_f64_bits(ops)?, &mut out);
}
"PUSH_BOOL" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
let v = parse_u32_any(ops)? as u8; emit_u16(CoreOpCode::PushBool as u16, &mut out); out.push(v);
}
"PUSH_I32" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::PushI32 as u16, &mut out); emit_i32(parse_i32_any(ops)?, &mut out);
}
"POP_N" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::PopN as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"PUSH_BOUNDED" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::PushBounded as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"GET_GLOBAL" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::GetGlobal as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"SET_GLOBAL" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::SetGlobal as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"GET_LOCAL" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::GetLocal as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"SET_LOCAL" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::SetLocal as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"CALL" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::Call as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"CALL_CLOSURE" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
let (k, v) = ops.split_once('=').ok_or_else(|| AsmError::InvalidOperand(ops.into()))?; if k.trim() != "argc" { return Err(AsmError::InvalidOperand(ops.into())); }
emit_u16(CoreOpCode::CallClosure as u16, &mut out); emit_u32(parse_u32_any(v)?, &mut out);
}
"MAKE_CLOSURE" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
let (a,b) = parse_keyvals(ops)?;
// Accept either order but require exact key names
let (fn_id, captures) = if a.starts_with("fn=") && b.starts_with("captures=") {
parse_pair(a, "fn", b, "captures")?
} else if a.starts_with("captures=") && b.starts_with("fn=") {
let (cap, fid) = parse_pair(a, "captures", b, "fn")?; (fid, cap)
} else { return Err(AsmError::InvalidOperand(ops.into())); };
emit_u16(CoreOpCode::MakeClosure as u16, &mut out); emit_u32(fn_id, &mut out); emit_u32(captures, &mut out);
}
"SPAWN" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
let (a,b) = parse_keyvals(ops)?;
let (fn_id, argc) = if a.starts_with("fn=") && b.starts_with("argc=") {
parse_pair(a, "fn", b, "argc")?
} else if a.starts_with("argc=") && b.starts_with("fn=") {
let (ac, fid) = parse_pair(a, "argc", b, "fn")?; (fid, ac)
} else { return Err(AsmError::InvalidOperand(ops.into())); };
emit_u16(CoreOpCode::Spawn as u16, &mut out); emit_u32(fn_id, &mut out); emit_u32(argc, &mut out);
}
"SLEEP" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::Sleep as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
"SYSCALL" => { if ops.is_empty() { return Err(AsmError::MissingOperand(line.into())); }
emit_u16(CoreOpCode::Syscall as u16, &mut out); emit_u32(parse_u32_any(ops)?, &mut out);
}
other => return Err(AsmError::UnknownMnemonic(other.into())),
}
}
Ok(out)
}

View File

@ -0,0 +1,115 @@
//! Deterministic disassembler for Prometeu Bytecode (PBC).
//!
//! Goals:
//! - Stable formatting across platforms (snapshot-friendly).
//! - Complete coverage of the Core ISA, including closures/coroutines.
//! - Roundtrip-safe with the paired `assembler` module.
//!
//! Format (one instruction per line):
//! - `MNEMONIC` for zero-operand instructions.
//! - `MNEMONIC <imm>` for 1-operand instructions (decimal unless stated).
//! - Special operand formats:
//! - `PUSH_F64 f64:0xhhhhhhhhhhhhhhhh` — exact IEEE-754 bits in hex (little-endian to_bits()).
//! - `MAKE_CLOSURE fn=<u32>, captures=<u32>`
//! - `SPAWN fn=<u32>, argc=<u32>`
//! - `CALL_CLOSURE argc=<u32>`
//! - `SYSCALL` is printed as `SYSCALL 0xhhhh` (numeric id in hex) to avoid cross-crate deps.
//!
//! Notes:
//! - All integers are printed in base-10 except where explicitly noted.
//! - Floats use exact bit-pattern format to prevent locale/rounding differences.
//! - Ordering is the canonical decode order; no address prefixes are emitted.
use crate::decode_next;
use crate::isa::core::{CoreOpCode, CoreOpCodeSpecExt};
use crate::DecodeError;
fn fmt_f64_bits(bits: u64) -> String {
// Fixed-width 16 hex digits, lowercase.
format!("f64:0x{bits:016x}")
}
fn format_operand(op: CoreOpCode, imm: &[u8]) -> String {
match op {
CoreOpCode::Jmp | CoreOpCode::JmpIfFalse | CoreOpCode::JmpIfTrue => {
let v = u32::from_le_bytes(imm.try_into().unwrap());
format!("{}", v)
}
CoreOpCode::PushI64 => {
let v = i64::from_le_bytes(imm.try_into().unwrap());
format!("{}", v)
}
CoreOpCode::PushF64 => {
let v = u64::from_le_bytes(imm.try_into().unwrap());
fmt_f64_bits(v)
}
CoreOpCode::PushBool => {
let v = imm[0];
format!("{}", v)
}
CoreOpCode::PushI32 => {
let v = i32::from_le_bytes(imm.try_into().unwrap());
format!("{}", v)
}
CoreOpCode::PopN
| CoreOpCode::PushConst
| CoreOpCode::PushBounded
| CoreOpCode::GetGlobal
| CoreOpCode::SetGlobal
| CoreOpCode::GetLocal
| CoreOpCode::SetLocal
| CoreOpCode::Call
| CoreOpCode::Sleep => {
let v = u32::from_le_bytes(imm.try_into().unwrap());
format!("{}", v)
}
CoreOpCode::MakeClosure => {
let fn_id = u32::from_le_bytes(imm[0..4].try_into().unwrap());
let cap = u32::from_le_bytes(imm[4..8].try_into().unwrap());
format!("fn={}, captures={}", fn_id, cap)
}
CoreOpCode::CallClosure => {
let argc = u32::from_le_bytes(imm.try_into().unwrap());
format!("argc={}", argc)
}
CoreOpCode::Spawn => {
let fn_id = u32::from_le_bytes(imm[0..4].try_into().unwrap());
let argc = u32::from_le_bytes(imm[4..8].try_into().unwrap());
format!("fn={}, argc={}", fn_id, argc)
}
CoreOpCode::Syscall => {
let id = u32::from_le_bytes(imm.try_into().unwrap());
// Hex id stable, avoids dependency on HAL metadata.
format!("0x{:04x}", id)
}
_ => {
// Fallback: raw immediate hex (little-endian, as encoded)
let mut s = String::with_capacity(2 + imm.len() * 2);
s.push_str("0x");
for b in imm {
use core::fmt::Write as _;
let _ = write!(&mut s, "{:02x}", b);
}
s
}
}
}
/// Disassembles a contiguous byte slice (single function body) into deterministic text.
pub fn disassemble(bytes: &[u8]) -> Result<String, DecodeError> {
let mut pc = 0usize;
let mut out = Vec::new();
while pc < bytes.len() {
let instr = decode_next(pc, bytes)?;
let name = instr.opcode.spec().name;
let imm_len = instr.opcode.spec().imm_bytes as usize;
if imm_len == 0 {
out.push(name.to_string());
} else {
let ops = format_operand(instr.opcode, instr.imm);
out.push(format!("{} {}", name, ops));
}
pc = instr.next_pc;
}
Ok(out.join("\n"))
}

View File

@ -7,12 +7,16 @@ mod opcode_spec;
mod program_image; mod program_image;
mod value; mod value;
pub mod isa; // canonical ISA boundary (core and future profiles) pub mod isa; // canonical ISA boundary (core and future profiles)
mod disassembler;
mod assembler;
pub use abi::{ pub use abi::{
TrapInfo, TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_ILLEGAL_INSTRUCTION, TRAP_INVALID_FUNC, TrapInfo, TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_ILLEGAL_INSTRUCTION, TRAP_INVALID_FUNC,
TRAP_INVALID_LOCAL, TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_STACK_UNDERFLOW, TRAP_TYPE, TRAP_INVALID_LOCAL, TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_STACK_UNDERFLOW, TRAP_TYPE,
}; };
pub use decoder::{decode_next, DecodeError}; pub use decoder::{decode_next, DecodeError};
pub use disassembler::disassemble;
pub use assembler::{assemble, AsmError};
pub use layout::{compute_function_layouts, FunctionLayout}; pub use layout::{compute_function_layouts, FunctionLayout};
pub use model::{BytecodeLoader, FunctionMeta, LoadError}; pub use model::{BytecodeLoader, FunctionMeta, LoadError};
pub use program_image::ProgramImage; pub use program_image::ProgramImage;

View File

@ -0,0 +1,42 @@
use prometeu_bytecode::{assemble, disassemble};
use prometeu_bytecode::isa::core::CoreOpCode;
fn emit(op: CoreOpCode, imm: Option<&[u8]>, out: &mut Vec<u8>) {
out.extend_from_slice(&(op as u16).to_le_bytes());
if let Some(bytes) = imm { out.extend_from_slice(bytes); }
}
#[test]
fn roundtrip_disasm_assemble_byte_equal_with_closures_and_coroutines() {
// Program: PUSH_I32 7; MAKE_CLOSURE fn=1,captures=0; CALL_CLOSURE argc=1;
// SPAWN fn=2,argc=1; YIELD; SLEEP 3; SYSCALL 0x1003; FRAME_SYNC; HALT
let mut prog = Vec::new();
emit(CoreOpCode::PushI32, Some(&7i32.to_le_bytes()), &mut prog);
// MAKE_CLOSURE (fn=1, captures=0)
let mut mc = [0u8; 8];
mc[0..4].copy_from_slice(&1u32.to_le_bytes());
mc[4..8].copy_from_slice(&0u32.to_le_bytes());
emit(CoreOpCode::MakeClosure, Some(&mc), &mut prog);
// CALL_CLOSURE argc=1
emit(CoreOpCode::CallClosure, Some(&1u32.to_le_bytes()), &mut prog);
// SPAWN (fn=2, argc=1)
let mut sp = [0u8; 8];
sp[0..4].copy_from_slice(&2u32.to_le_bytes());
sp[4..8].copy_from_slice(&1u32.to_le_bytes());
emit(CoreOpCode::Spawn, Some(&sp), &mut prog);
// YIELD
emit(CoreOpCode::Yield, None, &mut prog);
// SLEEP 3
emit(CoreOpCode::Sleep, Some(&3u32.to_le_bytes()), &mut prog);
// SYSCALL gfx.draw_line (0x1003)
emit(CoreOpCode::Syscall, Some(&0x1003u32.to_le_bytes()), &mut prog);
// FRAME_SYNC
emit(CoreOpCode::FrameSync, None, &mut prog);
// HALT
emit(CoreOpCode::Halt, None, &mut prog);
let text = disassemble(&prog).expect("disasm ok");
let rebuilt = assemble(&text).expect("assemble ok");
assert_eq!(rebuilt, prog, "re-assembled bytes must match original");
}

View File

@ -0,0 +1,39 @@
use prometeu_bytecode::disassemble;
use prometeu_bytecode::isa::core::CoreOpCode;
fn emit(op: CoreOpCode, imm: Option<&[u8]>, out: &mut Vec<u8>) {
out.extend_from_slice(&(op as u16).to_le_bytes());
if let Some(bytes) = imm { out.extend_from_slice(bytes); }
}
#[test]
fn snapshot_representative_program_is_stable() {
let mut prog = Vec::new();
emit(CoreOpCode::PushI32, Some(&7i32.to_le_bytes()), &mut prog);
// MAKE_CLOSURE (fn=1, captures=0)
let mut mc = [0u8; 8];
mc[0..4].copy_from_slice(&1u32.to_le_bytes());
mc[4..8].copy_from_slice(&0u32.to_le_bytes());
emit(CoreOpCode::MakeClosure, Some(&mc), &mut prog);
// CALL_CLOSURE argc=1
emit(CoreOpCode::CallClosure, Some(&1u32.to_le_bytes()), &mut prog);
// SPAWN (fn=2, argc=1)
let mut sp = [0u8; 8];
sp[0..4].copy_from_slice(&2u32.to_le_bytes());
sp[4..8].copy_from_slice(&1u32.to_le_bytes());
emit(CoreOpCode::Spawn, Some(&sp), &mut prog);
// YIELD
emit(CoreOpCode::Yield, None, &mut prog);
// SLEEP 3
emit(CoreOpCode::Sleep, Some(&3u32.to_le_bytes()), &mut prog);
// SYSCALL 0x1003
emit(CoreOpCode::Syscall, Some(&0x1003u32.to_le_bytes()), &mut prog);
// FRAME_SYNC
emit(CoreOpCode::FrameSync, None, &mut prog);
// HALT
emit(CoreOpCode::Halt, None, &mut prog);
let text = disassemble(&prog).expect("disasm ok");
let expected = "PUSH_I32 7\nMAKE_CLOSURE fn=1, captures=0\nCALL_CLOSURE argc=1\nSPAWN fn=2, argc=1\nYIELD\nSLEEP 3\nSYSCALL 0x1003\nFRAME_SYNC\nHALT";
assert_eq!(text, expected);
}

View File

@ -1,61 +1,3 @@
# PR-8.1 — Disassembler (Roundtrip + Snapshot Reliability)
## Briefing
The disassembler must be:
* Deterministic
* Complete
* Roundtrip-safe (encode → decode → encode)
* Snapshot-friendly
It must fully support:
* New opcodes (MAKE_CLOSURE, CALL_CLOSURE, SPAWN, YIELD, SLEEP)
* Updated syscall representation
* Closure and coroutine instructions
## Target
1. Update disassembler to support all new opcodes.
2. Guarantee stable formatting.
3. Add roundtrip validation tests.
Formatting rules:
* No unstable ordering.
* No implicit formatting differences across platforms.
* Explicit numeric representation where required.
## Acceptance Checklist
* [ ] All opcodes supported.
* [ ] Deterministic formatting.
* [ ] Roundtrip encode/decode test passes.
* [ ] Snapshot tests stable.
## Tests
1. Encode → disasm → reassemble → byte-equal check.
2. Snapshot test for representative programs.
3. Closure/coroutine disasm coverage.
## Junie Instructions
You MAY:
* Modify disassembler module.
* Add snapshot tests.
You MUST NOT:
* Change bytecode encoding format.
* Introduce formatting randomness.
If any opcode semantics unclear, STOP and ask.
---
# PR-8.3 — Layered Test Suite Architecture # PR-8.3 — Layered Test Suite Architecture
## Briefing ## Briefing