2008-11-11 20 views
24

me gustaría declarar un registro en Delphi que contiene la misma disposición que tiene en C.¿Cómo se simulan los campos de bits en los registros Delphi?

Para los interesados: Este disco forma parte de un sindicato en el registro LDT_ENTRY del sistema operativo Windows. (Necesito usar este registro en Delphi porque estoy trabajando en un emulador de Xbox en Delphi - ver proyecto Dxbx en sourceforge).

De todos modos, el registro en cuestión se define como:

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; 

Por lo que yo sé, no hay campos de bits posibles en Delphi. Yo probé esto:

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; 

Pero, por desgracia: su tamaño llega a ser de 10 bytes, en lugar de los 4. esperada Me gustaría saber cómo debo declarar el registro, por lo que consigo un registro con el mismo diseño , el mismo tamaño y los mismos miembros Preferiblemente sin cargas de getter/setters.

TIA.

Respuesta

28

¡Gracias a todos!

Basándose en esta información, I reducido este a:

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; 

El índice se codifica como sigue: (BitOffset shl 8) + NrBits. Donde 1 = < NrBits < = 32 y 0 = < BitOffset < = 31

Ahora, puede obtener y establecer estos bits de la siguiente manera:

{$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; 

bastante ingenioso, ¿no le parece?!? !

PD: Rudy Velthuis ahora incluye una versión revisada de esto en su excelente "Pitfalls of converting"-article.

+0

¡Es una muy buena idea! – gabr

+0

Gracias por el cumplido. Cometí algunos errores en el código, que he solucionado ahora, ¡Salud! – PatrickvL

+0

Gracias, esto es muy útil. "Flags" no debe ser un tipo entero? – JustMe

0

Bueno, básicamente tiene que ir al grano con la manipulación de bits.

¿Por qué, específicamente, necesita conservar esa estructura?

Si solo necesita hablar con un programa heredado que habla en este dialecto (TCP/IP o similar), o almacena datos de esta manera (archivos, etc.), entonces asignaría una estructura Delphi normal a una versión de bit compatible. En otras palabras, usaría una estructura Delphi normalmente estructurada en la memoria y escribiría un código para escribir y leer esa estructura de una manera compatible.

Si necesita guardar memoria, yo haría get getters y setters que manipulan bits de enteros internos o similares. Esto tendrá un impacto en el rendimiento, pero no mucho más de lo que tendría el programa C original, la única diferencia es que la manipulación de bits se agregaría mediante la magia del compilador en la versión C, mientras que usted deberá escribirla usted mismo.

Si no tiene muchos registros en la memoria y no necesita hablar con otro programa, usaría una estructura Delphi natural. Compensación para un mayor rendimiento será más memoria utilizada.

Pero todo depende de sus criterios.

En cualquier caso, no podrá utilizar el compilador Delphi para realizar el mismo trabajo que el compilador de C.

PACKED RECORD, sugerido por otro aquí, no hace eso, y nunca fue intencionado. Solo eliminará el relleno de alineación para poner enteros en los límites de 32 bits y similares, pero no empacará varios campos en un byte.

Tenga en cuenta que una forma común de hacerlo es a través de Delphi SETS, que implementan internamente el uso de campos de bits. Nuevamente, tendrá un código diferente a la variante C.

14

Rudy's Delphi Corner es el mejor recurso que conozco con respecto a la interoperabilidad Delphi y C/C++. Su Pitfalls of conversion es una lectura obligada cuando se usan API C/C++ en Delphi. El capítulo que más le interesará es Records and alignment -> Bitfields, pero le ruego que lea todo el asunto de arriba a abajo, dos veces. Los otros artículos definitivamente también valen la pena la inversión de tiempo.

5

Ok, mi manipulación de bits está un poco oxidada, así que podría haber invertido los bytes. Pero el código a continuación da la idea general:

type 
    TBits = record 
    private 
    FBaseMid  : Byte; 
    FTypeDplPres : Byte; 
    FLimitHiSysEa: Byte; 
    FBaseHi  : Byte; 

    function GetType: Byte; 
    procedure SetType(const AType: Byte); 
    function GetDpl: Byte; 
    procedure SetDbl(const ADpl: Byte); 
    function GetBit1(const AIndex: Integer): Boolean; 
    procedure SetBit1(const AIndex: Integer; const AValue: Boolean); 
    function GetLimitHi: Byte; 
    procedure SetLimitHi(const AValue: Byte); 
    function GetBit2(const AIndex: Integer): Boolean; 
    procedure SetBit2(const AIndex: Integer; const AValue: Boolean); 

    public 
    property BaseMid: Byte read FBaseMid write FBaseMid; 
    property &Type: Byte read GetType write SetType; // 0..31 
    property Dpl: Byte read GetDpl write SetDbl; // 0..3 
    property Pres: Boolean index 128 read GetBit1 write SetBit1; 
    property LimitHi: Byte read GetLimitHi write SetLimitHi; // 0..15 

    property Sys: Boolean index 16 read GetBit2 write SetBit2; 
    property Reserved0: Boolean index 32 read GetBit2 write SetBit2; 
    property DefaultBig: Boolean index 64 read GetBit2 write SetBit2; 
    property Granularity: Boolean index 128 read GetBit2 write SetBit2; 
    property BaseHi: Byte read FBaseHi write FBaseHi; 
    end; 

    function TBits.GetType: Byte; 
    begin 
    Result := (FTypeDplPres shr 3) and $1F; 
    end; 

    procedure TBits.SetType(const AType: Byte); 
    begin 
    FTypeDplPres := (FTypeDplPres and $07) + ((AType and $1F) shr 3); 
    end; 

    function TBits.GetDpl: Byte; 
    begin 
    Result := (FTypeDplPres and $06) shr 1; 
    end; 

    procedure TBits.SetDbl(const ADpl: Byte); 
    begin 
    FTypeDblPres := (FTypeDblPres and $F9) + ((ADpl and $3) shl 1); 
    end; 

    function TBits.GetBit1(const AIndex: Integer): Boolean; 
    begin 
    Result := FTypeDplPres and AIndex = AIndex; 
    end; 

    procedure TBits.SetBit1(const AIndex: Integer; const AValue: Boolean); 
    begin 
    if AValue then 
     FTypeDblPres := FTypeDblPres or AIndex 
    else 
     FTypeDblPres := FTypeDblPres and not AIndex; 
    end; 

    function TBits.GetLimitHi: Byte; 
    begin 
    Result := (FLimitHiSysEa shr 4) and $0F; 
    end; 

    procedure TBits.SetLimitHi(const AValue: Byte); 
    begin 
    FLimitHiSysEa := (FLimitHiSysEa and $0F) + ((AValue and $0F) shr 4); 
    end; 

    function TBits.GetBit2(const AIndex: Integer): Boolean; 
    begin 
    Result := FLimitHiSysEa and AIndex = AIndex; 
    end; 

    procedure TBits.SetBit2(const AIndex: Integer; const AValue: Boolean); 
    begin 
    if AValue then 
     FLimitHiSysEa := FLimitHiSysEa or AIndex 
    else 
     FLimitHiSysEa := FLimitHiSysEa and not AIndex; 
    end; 
Cuestiones relacionadas