如何模拟Delphi记录中的位域? [英] How to simulate bit-fields in Delphi records?

查看:181
本文介绍了如何模拟Delphi记录中的位域?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



对于那些感兴趣的人:这个记录是一个工会的一部分。 Windows操作系统的LDT_ENTRY记录。 (我需要在Delphi中使用这个记录,因为我正在Delphi中使用Xbox模拟器 - 请参阅sourceforge上的项目Dxbx。)



无论如何,有问题的记录是定义为:

  struct 
{
DWORD BaseMid:8;
DWORD类型:5;
DWORD Dpl:2;
DWORD Pres:1;
DWORD LimitHi:4;
DWORD Sys:1;
DWORD Reserved_0:1;
DWORD Default_Big:1;
DWORD粒度:1;
DWORD BaseHi:8;
}
位;

据我所知,Delphi中没有位域可能。我确实尝试过:

  Bits = record 
BaseMid:Byte; // 8位
_Type:0..31; // 5位
Dpl:0..3; // 2位
Pres:Boolean; // 1 bit
LimitHi:0..15; // 4位
Sys:Boolean; // 1 bit
Reserved_0:Boolean; // 1 bit
Default_Big:Boolean; // 1 bit
粒度:布尔值// 1 bit
BaseHi:Byte; // 8位
end;

但唉:它的大小变成10个字节,而不是预期的4.
我会喜欢知道我应该如何申报记录,以便我得到一个具有相同布局,大小和相同成员的记录。



TIA。

解决方案

感谢大家!



根据这些信息,我将其简化为:

  RBits = record 
public
BaseMid:BYTE;
private
标志:WORD;
函数GetBits(const aIndex:Integer):整数;
程序SetBits(const aIndex:Integer; const aValue:Integer);
public
BaseHi:BYTE;
属性_Type:整数索引$ 0005读取GetBits写入SetBits; //偏移量为5位
属性Dpl:整型索引$ 0502读取GetBits写入SetBits; //偏移量为2位
属性Pres:整型索引$ 0701读取GetBits写入SetBits; // 1位在偏移7
属性LimitHi:整数索引$ 0804读取GetBits写入SetBits; // 4位在偏移8
属性Sys:整数索引$ 0C01读取GetBits写入SetBits; // 1位在偏移量12
属性Reserved_0:整数索引$ 0D01读取GetBits写入SetBits; // 1位在偏移量13
属性Default_Big:整型索引$ 0E01读取GetBits写入SetBits; // 1位在偏移量14
属性粒度:整数索引$ 0F01读取GetBits写入SetBits; // 1位在偏移量15
end;

该索引编码如下:(BitOffset shl 8)+ NrBits 。其中1 <= NrBits <= 32且0 <= BitOffset = = 31



现在,我可以得到并设置这些位如下:

  {$ OPTIMIZATION ON} 
{$ OVERFLOWCHECKS OFF}
函数RBits.GetBits(const aIndex:Integer):整数;
var
偏移量:整数;
NrBits:整数;
面具:整数;
begin
NrBits:= aIndex和$ FF;
Offset:= aIndex shr 8;

掩码:=((1 shl NrBits) - 1);

结果:=(标志shr Offset)和Mask;
结束

procedure RBits.SetBits(const aIndex:Integer; const aValue:Integer);
var
偏移量:整数;
NrBits:整数;
面具:整数;
begin
NrBits:= aIndex和$ FF;
Offset:= aIndex shr 8;

掩码:=((1 shl NrBits) - 1);
Assert(aValue< = Mask);

标志:=(Flags和(not(Mask shl Offset)))或(aValue shl Offset);
结束

很漂亮,你不觉得吗? b $ b

PS:Rudy Velthuis现在在他的优秀转化的陷阱 - 粒子


I would like to declare a record in Delphi that contains the same layout as it has in C.

For those interested : This record is part of a union in the Windows OS's LDT_ENTRY record. (I need to use this record in Delphi because I'm working on an Xbox emulator in Delphi - see project Dxbx on sourceforge).

Anyway, the record in question is defined as:

    struct
    {
        DWORD   BaseMid : 8;
        DWORD   Type : 5;
        DWORD   Dpl : 2;
        DWORD   Pres : 1;
        DWORD   LimitHi : 4;
        DWORD   Sys : 1;
        DWORD   Reserved_0 : 1;
        DWORD   Default_Big : 1;
        DWORD   Granularity : 1;
        DWORD   BaseHi : 8;
    }
    Bits;

As far as I know, there are no bit-fields possible in Delphi. I did try this:

 Bits = record
      BaseMid: Byte; // 8 bits
      _Type: 0..31; // 5 bits
      Dpl: 0..3; // 2 bits
      Pres: Boolean; // 1 bit
      LimitHi: 0..15; // 4 bits
      Sys: Boolean; // 1 bit
      Reserved_0: Boolean; // 1 bit
      Default_Big: Boolean; // 1 bit
      Granularity: Boolean; // 1 bit
      BaseHi: Byte; // 8 bits
  end;

But alas: it's size becomes 10 bytes, instead of the expected 4. I would like to know how I should declare the record, so that I get a record with the same layout, the same size, and the same members. Preferrably without loads of getter/setters.

TIA.

解决方案

Thanks everyone!

Based on this information, I reduced this to :

RBits = record
public
  BaseMid: BYTE;
private
  Flags: WORD;
  function GetBits(const aIndex: Integer): Integer;
  procedure SetBits(const aIndex: Integer; const aValue: Integer);
public
  BaseHi: BYTE;
  property _Type: Integer index $0005 read GetBits write SetBits; // 5 bits at offset 0
  property Dpl: Integer index $0502 read GetBits write SetBits; // 2 bits at offset 5
  property Pres: Integer index $0701 read GetBits write SetBits; // 1 bit at offset 7
  property LimitHi: Integer index $0804 read GetBits write SetBits; // 4 bits at offset 8
  property Sys: Integer index $0C01 read GetBits write SetBits; // 1 bit at offset 12
  property Reserved_0: Integer index $0D01 read GetBits write SetBits; // 1 bit at offset 13
  property Default_Big: Integer index $0E01 read GetBits write SetBits; // 1 bit at offset 14
  property Granularity: Integer index $0F01 read GetBits write SetBits; // 1 bit at offset 15
end;

The index is encoded as follows : (BitOffset shl 8) + NrBits. Where 1<=NrBits<=32 and 0<=BitOffset<=31

Now, I can get and set these bits as follows :

{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
function RBits.GetBits(const aIndex: Integer): Integer;
var
  Offset: Integer;
  NrBits: Integer;
  Mask: Integer;
begin
  NrBits := aIndex and $FF;
  Offset := aIndex shr 8;

  Mask := ((1 shl NrBits) - 1);

  Result := (Flags shr Offset) and Mask;
end;

procedure RBits.SetBits(const aIndex: Integer; const aValue: Integer);
var
  Offset: Integer;
  NrBits: Integer;
  Mask: Integer;
begin
  NrBits := aIndex and $FF;
  Offset := aIndex shr 8;

  Mask := ((1 shl NrBits) - 1);
  Assert(aValue <= Mask);

  Flags := (Flags and (not (Mask shl Offset))) or (aValue shl Offset);
end;

Pretty nifty, don't you think?!?!

PS: Rudy Velthuis now included a revised version of this in his excellent "Pitfalls of converting"-article.

这篇关于如何模拟Delphi记录中的位域?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆