Config is now loaded at runtime via WebSocket (ConfigCtrlMessage) instead of from hardcoded TOML files. The emulator starts with no devices and waits for clients to send configuration. - Define FlatBuffers schemas: EmulationConfig, ComposeDeviceConfig, BaseDeviceConfig with recursive DeviceConfig union - Rename MemSegment.start → addr (flatcc builder/reader name collision) - Add ConfigCtrlMessage handler: validates STILL state, walks the device tree depth-first, assigns numeric IDs, responds with DeviceIdMappingNotif or ConfigLoadError - Add fb_build_config_device_id_mapping() and fb_build_config_error() FlatBuffer builders - Remove hardcoded device loading from main.c; iterate dynamically loaded devices in the exec loop - Fix double-free: freeConf() already frees the struct itself, remove redundant free() calls in config.c and base_device.c - Fix heap-buffer-overflow in device parseSpecsFromConfig: malloc for segment name was missing +1 for the null terminator Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
74 lines
2.8 KiB
Plaintext
74 lines
2.8 KiB
Plaintext
namespace hmmmm.config;
|
|
|
|
// Named offset within a segment — symbolic variable for protocol/tool use
|
|
// (e.g. PIN=0, PORT=1, DDR=2 in a GPIO device).
|
|
table NamedOffset {
|
|
name: string;
|
|
offset: uint32;
|
|
}
|
|
|
|
// A contiguous memory segment within a base device.
|
|
table MemSegment {
|
|
name: string;
|
|
addr: uint32; // base address in device flat address space
|
|
len: uint32;
|
|
word_len: uint8 = 1; // word size in bytes
|
|
executable: bool = false;
|
|
variables: [NamedOffset]; // optional symbolic offsets within segment
|
|
}
|
|
|
|
// ── Projections ──────────────────────────────────────────────────────────────
|
|
|
|
// Maps a segment from one device into the flat address space of another.
|
|
// After projection, accesses to [base_seg + shift .. shift+len) in base_at
|
|
// are transparently forwarded to target_at:target_seg.
|
|
table Projection {
|
|
base_at: string; // device where the projection appears
|
|
base_seg: string; // segment in that device that is the projection window
|
|
target_at: string; // device owning the real data
|
|
target_seg: string;
|
|
shift: uint32 = 0; // offset from base_seg start
|
|
}
|
|
|
|
// ── Intercepts ───────────────────────────────────────────────────────────────
|
|
|
|
enum InterceptOp: byte {
|
|
op_read = 0,
|
|
op_write = 1,
|
|
op_both = 2,
|
|
}
|
|
|
|
enum InterceptMode: byte {
|
|
shadow_copy = 1, // duplicate write to point_at:point_addr as well
|
|
shadow_replace = 2, // redirect access to point_at:point_addr instead
|
|
callback = 3, // invoke a handler registered by point_at device
|
|
}
|
|
|
|
table Intercept {
|
|
name: string;
|
|
op: InterceptOp = op_both;
|
|
mode: InterceptMode = callback;
|
|
base_at: string; // device where the triggering access occurs
|
|
base_seg: string;
|
|
base_addr: uint32;
|
|
point_at: string; // device that handles/receives the intercept
|
|
point_seg: string;
|
|
point_addr: uint32;
|
|
}
|
|
|
|
// ── Memory initialisation ─────────────────────────────────────────────────────
|
|
|
|
enum OverflowBehaviour: byte {
|
|
overflow_error = 0, // abort if file is larger than segment
|
|
overflow_ignore = 1, // stop reading at segment end, no error
|
|
overflow_wrap = 2, // wrap around and continue writing from segment start
|
|
}
|
|
|
|
// Load initial segment contents from a file (e.g. firmware binary, /dev/urandom).
|
|
table MemSetup {
|
|
device: string;
|
|
segment: string;
|
|
filepath: string;
|
|
overflow: OverflowBehaviour = overflow_error;
|
|
}
|