Ada - 如何显式打包位域记录类型?

Ada - how to explicitly pack a bit-field record type?

请考虑以下实验性 Ada 程序,该程序尝试创建具有明确定义的位字段的 32 位记录,创建一个并将其输出到文件流...

with System;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Streams.Stream_Io; use Ada.Streams.Stream_Io;

procedure Main is

   type Bit is mod    (2 ** 1);

   type Opcode_Number is mod (2 ** 4);
   type Condition_Number is mod (2 ** 4);
   type Operand is mod (2 ** 9);

   type RAM_Register is
      record
         Opcode : Opcode_Number;
         Z      : Bit;
         C      : Bit;
         R      : Bit;
         I      : Bit;
         Cond   : Condition_Number;
         Rsvd_1 : Bit;
         Rsvd_2 : Bit;
         Dest   : Operand;
         Src    : Operand;
      end record;

   for RAM_Register use
      record
         Opcode at 0 range 28 .. 31;
         Z      at 0 range 27 .. 27;
         C      at 0 range 26 .. 26;
         R      at 0 range 25 .. 25;
         I      at 0 range 24 .. 24;
         Cond   at 0 range 20 .. 23;
         Rsvd_1 at 0 range 19 .. 19;
         Rsvd_2 at 0 range 18 .. 18;
         Dest   at 0 range  9 .. 17;
         Src    at 0 range  0 ..  8;
      end record;
   for RAM_Register'Size use 32;
   for RAM_Register'Bit_Order use System.High_Order_First;

   --  ADA 2012 language reference 'full_type_declaration'
   --  (page 758, margin number 8/3) for RAM_Register
   pragma Atomic (RAM_Register);


   --   3         2         1         0
   --  10987654321098765432109876543210
   --  OOOOzcriCONDrrDDDDDDDDDsssssssss

   X : RAM_Register := (2#1000#,
                           2#1#,
                           2#1#,
                           2#1#,
                           2#1#,
                        2#1000#,
                           2#1#,
                           2#1#,
                   2#100000001#,
                   2#100000001#);

   The_File : Ada.Streams.Stream_IO.File_Type;
   The_Stream : Ada.Streams.Stream_IO.Stream_Access;

begin
   begin
      Open (The_File, Out_File, "test.dat");
   exception
      when others =>
         Create (The_File, Out_File, "test.dat");
   end;

   The_Stream := Stream (The_File);
   RAM_Register'Write (The_Stream, X);
   Close (The_File);
end Main;

我使用这里的信息:https://rosettacode.org/wiki/Object_serialization#Ada and here: https://en.wikibooks.org/wiki/Ada_Programming/Attributes/%27Bit_Order(最后一个例子)来创建上面的内容。

运行 代码并使用 xxd -g1 test.dat 检查输出给出以下 12 个字节的输出...

00000000: 08 01 01 01 01 08 01 01 01 01 01 01              ............

问题:

如何将这个 32 位记录作为 32 位写入流或从中读取,观察所有位域位置?想象一下,我正在通过 RS-232 端口与微控制器通信,每个位都需要在正确的时间准确地位于正确的位置。语法 for RAM_Register use record... 似乎对 'Write 如何安排其输出没有影响。

如果我确实提供了自己的 'Read'Write 实现,那么 直接 是否与 'for RAM_Register 使用记录相矛盾...`代码?

您可能必须将实例转换为无符号整数(通过未经检查的转换),然后将无符号整数写入流。 Write 的默认实现忽略了表示子句(另见 RM 13 9/3):

For composite types, the Write or Read attribute for each component is called in canonical order, [...]

所以,添加

with Interfaces; use Interfaces;
with Ada.Unchecked_Conversion;

并将RAM_Register定义为

   type RAM_Register is
      record
         Opcode : Opcode_Number;
         Z      : Bit;
         C      : Bit;
         R      : Bit;
         I      : Bit;
         Cond   : Condition_Number;
         Rsvd_1 : Bit;
         Rsvd_2 : Bit;
         Dest   : Operand;
         Src    : Operand;
      end record with Atomic;  

   procedure Write
     (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
      Item   : RAM_Register);

   for RAM_Register'Write use Write;

   for RAM_Register use
      record         
         Opcode at 0 range 28 .. 31;
         Z      at 0 range 27 .. 27;
         C      at 0 range 26 .. 26;
         R      at 0 range 25 .. 25;
         I      at 0 range 24 .. 24;
         Cond   at 0 range 20 .. 23;
         Rsvd_1 at 0 range 19 .. 19;
         Rsvd_2 at 0 range 18 .. 18;
         Dest   at 0 range  9 .. 17;
         Src    at 0 range  0 ..  8;
      end record;   

   for RAM_Register'Size use 32;
   for RAM_Register'Bit_Order use System.High_Order_First; 

   -----------
   -- Write --
   -----------

   procedure Write
     (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
      Item   : RAM_Register)
   is

      function To_Unsigned_32 is
        new Ada.Unchecked_Conversion (RAM_Register, Unsigned_32); 

      U32 : Unsigned_32 := To_Unsigned_32 (Item);

   begin   
      Unsigned_32'Write (Stream, U32);        
   end Write;

这会产生

$ xxd -g1 test.dat
00000000: 01 03 8e 8f                                      ....

注意:位序可能已经颠倒了,因为我不得不评论方面规范for RAM_Register'Bit_Order use System.High_Order_First;