let page_size = 4096 (* Field offsets *) let off_heartbeat = 0x000 let off_heartbeat_ack = 0x008 let off_time_version = 0x010 let off_time_seconds = 0x014 let off_time_nanos = 0x01C let off_guest_status = 0x020 let off_host_cmd = 0x024 let off_guest_cmd_ack = 0x028 let off_health_string = 0x100 let health_string_len = 256 (* uint64 big-endian accessors *) let heartbeat buf = Bytes.get_int64_be buf off_heartbeat let set_heartbeat buf v = Bytes.set_int64_be buf off_heartbeat v let heartbeat_ack buf = Bytes.get_int64_be buf off_heartbeat_ack let set_heartbeat_ack buf v = Bytes.set_int64_be buf off_heartbeat_ack v (* uint32 big-endian accessors *) let time_version buf = Int32.to_int (Bytes.get_int32_be buf off_time_version) let set_time_version buf v = Bytes.set_int32_be buf off_time_version (Int32.of_int v) let time_seconds buf = Bytes.get_int64_be buf off_time_seconds let set_time_seconds buf v = Bytes.set_int64_be buf off_time_seconds v let time_nanos buf = Int32.to_int (Bytes.get_int32_be buf off_time_nanos) let set_time_nanos buf v = Bytes.set_int32_be buf off_time_nanos (Int32.of_int v) let guest_status buf = Int32.to_int (Bytes.get_int32_be buf off_guest_status) let set_guest_status buf v = Bytes.set_int32_be buf off_guest_status (Int32.of_int v) let host_cmd buf = Int32.to_int (Bytes.get_int32_be buf off_host_cmd) let set_host_cmd buf v = Bytes.set_int32_be buf off_host_cmd (Int32.of_int v) let guest_cmd_ack buf = Int32.to_int (Bytes.get_int32_be buf off_guest_cmd_ack) let set_guest_cmd_ack buf v = Bytes.set_int32_be buf off_guest_cmd_ack (Int32.of_int v) let health_string buf = let s = Bytes.sub_string buf off_health_string health_string_len in (* Find null terminator *) match String.index_opt s '\x00' with | Some i -> String.sub s 0 i | None -> s let set_health_string buf s = let len = min health_string_len (String.length s) in Bytes.blit_string s 0 buf off_health_string len; (* Null-terminate *) if len < health_string_len then Bytes.fill buf (off_health_string + len) (health_string_len - len) '\x00' (* Mission time with seqlock *) type mission_time = { seconds : int64; nanos : int } let write_mission_time buf t = let v = time_version buf + 1 in set_time_version buf v; (* odd = write in progress *) set_time_seconds buf t.seconds; set_time_nanos buf t.nanos; set_time_version buf (v + 1) (* even = write complete *) let read_mission_time buf = let rec loop () = let v1 = time_version buf in if v1 land 1 <> 0 then loop () (* spin-wait: host is mid-write *) else let seconds = time_seconds buf in let nanos = time_nanos buf in let v2 = time_version buf in if v1 <> v2 then loop () (* torn read, retry *) else { seconds; nanos } in loop () (* Command word bits *) let cmd_shutdown = 1 lsl 0 let cmd_param_reload = 1 lsl 1 let cmd_dp_ack = 1 lsl 2