News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

h2inc+

Started by Biterider, March 29, 2024, 10:45:35 PM

Previous topic - Next topic

jimg

If Siekmanski is still around, he would be the one to ask for sure.  I'm  pretty sure I stole the code from him (with his permission).

jimg

Here's one of the links serial

Greenhorn

#17
Quote from: Biterider on April 09, 2024, 07:14:52 AMCan you confirm that your reversed RECORD really works?

zBITRECORD RECORD zDummy2:17,zAbortOnError:1,zRtsControl:2,zNull:1,zErrorChar:1,zInX:1,zOutX:1,zTXContinueOnXoff:1,zDsrSensitivity:1,zDtrControl:2,zOutxDsrFlow:1,zOutxCtsFlow:1,zParity:1,zBinary:1

Bitfields/Records in assembly must be defined in the reverse order than its definition in C.

For an example I attached usp10.inc that works.
usp10.h for comparison: usp10.h
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

Greenhorn

Quote from: Biterider on April 08, 2024, 09:37:01 PMThe next issue is that all names in the RECORD directive must be unique.

Regarding Bitfields/Records MASM is a mess with a lot of hassle to deal with.

When I was translating my own windows headers with japheth's h2incx tool I had to solve this problem manually. I just set a numeric suffix to the often doubled fieldname "reserved", so like reservedn:7, where n is a numeric.
The Problem here is: how to remember which suffix the specific fieldname has.

AFAIK AsmC solved that problem by making fieldnames in records non-unique. But this is incompatible to MASM and it's clones.

Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

Greenhorn

Another issue is the alignment, i.e. if a record is defined within a structure, respectively in general.

If a record's size in C is defined as DWORD/int, Japheth solved it like this:

ATTR struct
    _ATTR_R0    RECORD    \
    reserved6    : 6,        ;// possible underline/other styles  (must be NULL)
    eol        : 1,        ;// when set, prevents cursor from selecting character. only valid for last char in line
    ctrl        : 1,        ;// show as an isolated control-character
    sel        : 1,        ;// selection flag  (yes/no)
    font_    : 7,    ;// font-index
    len_        : 16    ;// length of this run  (in code-units)
    union
        DWORD    ?
        _ATTR_R0    <>
    ends

    fg    COLORREF    ?
    bg    COLORREF    ?
ATTR ends
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

Biterider

Hi Greenhorn
Looking at this memory arrangement, it seems to me that it is not an alignment problem of the RECORD itself, but rather the alignment of the following members. Am I right?

Biterider

Greenhorn

Quote from: Biterider on April 10, 2024, 03:03:56 AMHi Greenhorn
Looking at this memory arrangement, it seems to me that it is not an alignment problem of the RECORD itself, but rather the alignment of the following members. Am I right?

Biterider

I'm not sure about it, it's too long ago I struggled with this.
It could also be that it's because of "type" of the record and allocating memory for it, maybe in case that one defines a record with just 28 bit leaving the remaining 4 bit undefined.

IIRC, h2incx has an option/switch to these union to a record or not.

Maybe @_japheth can tell more about it.

The MASM Programmer's Guide does not mention this "union thing".
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

Greenhorn

Edited last post ...
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

Greenhorn

This is the documentation for the "r" switch for h2incX:

-r: size-safe RECORD definitions. May be required if a C bitfield isn't
     fully defined, that is, only some bits are declared. With this option
     set the record is enclosed in a union together with its type.
     example (excerpt from structure MENUBARINFO in winuser.h):
     
         BOOL  fBarFocused:1;
         BOOL  fFocused:1;
         
     is now translated to:   
     
         MENUBARINFO_R0 RECORD fBarFocused:1,fFocused:1
         union                 ;added by -r switch
             BOOL ?            ;added by -r switch
             MENUBARINFO_R0 <>
         ends                  ;added by -r switch
         
     So MASM will reserve space for a BOOL (which is 4 bytes). Without
     the -r option MASM would pack the bits in 1 byte only.
     

So, it's about size as I suspected ...
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

Biterider

Hi Greenhorn
Thanks for the info. In the meantime, I'm experimenting with C and Masm to see how they implement RECORD bit manipulation. It's really not that complex once you understand that they only work with registers and not memory. This eliminates all the complexity of endianness.

Biterider

Greenhorn

With my little skills in macros I've tinkered by that time this ones.

;-------------------------------------------------------------------
;--- GETFIELD() macro function to retrieve a value from a RECORD
;--- example: GETFIELD(SomeRec.Fieldname)
;-------------------------------------------------------------------

GETFIELD macro FieldDotFieldname:REQ
LOCAL reg, is, dot, field, fieldname, size_field
  is INSTR 2, <FieldDotFieldname>, <.> ;-- first char must NOT be a '.'
  if is eq 0
    .err <GETFIELD: Field.Fieldname required. >
  endif
  while is gt 0 ;-- Loop through argument and find last '.', if any.
    dot = is ;-- Store last position of '.'
    is INSTR is+1, <FieldDotFieldname>, <.> ;-- Start search at last position of '.'
  endm
  field SUBSTR <FieldDotFieldname>, 1, dot-1 ;-- get the record
  fieldname SUBSTR <FieldDotFieldname>, dot+1 ;-- get the record member
  ;--- determine the size of the bit field
  size_field = (SIZEOF (TYPEOF (field))) ;-- get the size of the record
  if (SIZEOF (TYPEOF (field))) EQ 1
    reg equ <al> ;-- Record is 1 byte size, set al to store the result.
  elseif (SIZEOF (TYPEOF (field))) EQ 2
    reg equ <ax> ;-- Record is 2 byte size, set ax to store the result.
  elseif (SIZEOF (TYPEOF (field))) EQ 4
    reg equ <eax> ;-- Record is 4 byte size, set eax to store the result.
  elseif (SIZEOF (TYPEOF (field))) EQ 8
    reg equ <rax> ;-- Record is 8 byte size, set rax to store the result.
  endif
  ifdifi field, reg
    mov  reg, field ;-- move only if register differs
  endif
ifdef __ASMC__
    and  reg, .MASK fieldname ;-- AND the register by the mask
else
    and  reg, MASK fieldname ;-- AND the register by the mask
endif
;;  if (IS_ARG_SIGNED_PTR(FieldDotFieldname) eq 1)
;;    if (.WIDTH fieldname + fieldname) lt size_field * 8
;;      shl  reg, size_field * 8 - .WIDTH fieldname - fieldname ;-- shift to left (signed)
;;      sar  reg, size_field * 8 - .WIDTH fieldname ;-- shift to right (signed)
;;    else
;;      sar  reg, fieldname ;-- shift to right (signed)
;;    endif
;;  else
    if (fieldname) gt 0
      shr  reg, fieldname ;-- shift to right (unsigned)
    endif
;;  endif
  exitm <reg>
endm

;-------------------------------------------------------------------
;--- SETFIELD() macro function to set a value to a RECORD
;--- example: SETFIELD(SomeRec.Fieldname, 1)
;-------------------------------------------------------------------

SETFIELD macro dest:REQ, value:REQ
LOCAL field_dot_name, val, reg, is, dot, field, fieldname, size_field, size_val, movxx
  is INSTR 1, <dest>, <!(> ;-- Exclude leading bracket, if any
  if is gt 0
    field_dot_name SUBSTR <dest>, is+1
  else
    field_dot_name EQU <dest>
  endif
  is INSTR 1, <value>, <!)> ;-- Exclude trailing bracket, if any
  if is gt 0
    val SUBSTR <value>, 1, is-1
  else
    val EQU <value>
  endif
  is INSTR 2, field_dot_name, <.> ;-- first char must NOT be a '.'
  if is eq 0
    .err <SETFIELD: Field.Fieldname required. >
  else
    while is gt 0 ;-- Loop through argument and find last '.', if any.
      dot = is ;-- Store last position of '.'
      is INSTR is+1, field_dot_name, <.> ;-- Start search at last position of '.'
    endm
  endif
  field SUBSTR field_dot_name, 1, dot-1 ;-- get the record
  fieldname SUBSTR field_dot_name, dot+1 ;-- get the record member
  size_field = (SIZEOF (TYPEOF (field))) ;-- get the size of the record
  if ((OPATTR (val)) and 00000100b) ;; argument is constant
size_val = size_field ;-- if the value to set is a constant, set the size equal to the record size.
  else
  size_val = (SIZEOF (TYPEOF (val))) ;-- the value to set is a memory location or a register
  endif ;;(OPATTR (val)) and 00000100b
  if size_field EQ 1
    reg equ <al> ;-- size of the record is 1 byte
    movxx equ <mov> ;-- set instruction to mov
 elseif size_field EQ 2
    reg equ <ax> ;-- size of the record is 2 byte
    if size_val EQ 1
      movxx equ <movzx> ;-- set instruction to movzx
    else
      movxx equ <mov> ;-- set instruction to mov
    endif
  elseif size_field EQ 4
     reg equ <eax> ;-- size of the record is 4 byte
    if size_val LE 2
      movxx equ <movzx> ;-- set instruction to movzx
    else
      movxx equ <mov> ;-- set instruction to mov
    endif
  elseif size_field EQ 8
    reg equ <rax> ;-- size of the record is 8 byte
    if size_val LE 2
      movxx equ <movzx> ;-- set instruction to movzx
    elseif size_val EQ 4
      movxx equ <movsxd> ;-- set instruction to movsxd
    else
      movxx equ <mov> ;-- set instruction to mov
    endif
  else
    .err <SETFIELD: wrong size of argument 1.>
  endif
  ifdifi val, reg
    movxx  reg, val ;-- mov the value to set into register (only if it differs)
  endif
  if (.WIDTH fieldname) gt 0
    shl  reg, fieldname ;-- shift to right
ifdef __ASMC__
    and  field, not .MASK fieldname ;-- and the the record by the mask
else
    and  field, not MASK fieldname ;-- and the the record by the mask
endif
  else
    .err <SETFIELD: fieldname required.>
  endif
    or  field, reg ;-- add the value to set (end of macro SETFIELD)
endm


Example

add  attrLen, GETFIELD(dword ptr [rcx+rdx].ATTR.len_)
...
SETFIELD(DWORD ptr [rdx+r8].ATTR.len_, 1)
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

Biterider

Hi
What I didn't know was that an unnamed field with zero width would force alignment, which I verified by testing with an MS C compiler.

https://en.wikipedia.org/wiki/C_syntax#:~:text=Unnamed%20fields%20consisting,fit%20in%20it.


Biterider

Greenhorn

From your link:

QuoteAs a special exception to the usual C syntax rules, it is implementation-defined whether a bit field declared as type int, without specifying signed or unsigned, is signed or unsigned. Thus, it is recommended to explicitly specify signed or unsigned on all structure members for portability.

When I was writing these GET/SETFIELD macros I debugged with VC a C library using bitfields typed as int (signed).
And in deed the compiler used arithmetic for signed operations and I tried to consider this for my macros.
But after thinking a while about it, it made absolutely no sense to me.

First, for a signed value the width of a fieldname must be greater than 1 to store the signed bit. So, what to do if a bitfield is typed as int signed and the fieldname you want to manipulate or retrieve is just one bit?

Second, for example your fieldname is 3 bits in width and has '101' set. Since it is typed as an int you load it into EAX. This will never give you a signed result.

So it makes no sense to me. That's why I commented the 'signed arithmetic' out in the macro.

Correct me if I'm wrong.
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

Greenhorn

What I'm trying to say is: Bitfields/Records typed as signed makes no sense.
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

jj2007

Quote from: Greenhorn on April 10, 2024, 06:23:57 AMGETFIELD() macro function to retrieve a value from a RECORD

QuoteI'm just wondering, though: who on earth would really want to use bit fields in an actual program? Thinking back, the only time I've ever used them was to unpack the old DOS date/time structures--remember those? Even then, I never used the assembler, but just defined my own EQUs and did the necessary shifting and masking by hand.

I am curious if the author recognises his text :bgrin:

Quote from: Greenhorn on April 10, 2024, 07:06:52 AMyour fieldname is 3 bits in width and has '101' set

111=-3
110=-2
101=-1
100=-0
000=0
001=1
010=2
011=3

I admit it's a rather "forced" interpretation ;-)