I have a UInt64 value and want to join and separate the UInt64 into 2 DWords?

Is their an instruction - I don’t know of one?
var
MyNo; UInt64
LDWord, SDWord: DWord;

MyNo := 0’
MyNo := LDWord ROL 32;
MyNo := MyNo and SDWord;

SDWord := MyNo and $FFFFFFFF;
LDWord := (MyNo ROR 32) and $FFFFFFFF;

Can I do a rotate 32 like this is their a better way?

Hi Lex,

To do a bitwise rotate in Delphi use shr. e.g.

LDWord := MyNo shr 32;

There’s no magic/easy way to do this as far as I know. The alternative would be to use a packed record containing a variant part (multiple variables overlaid on the same data - use one value of the variant part as UInt64, the other as 2 x DWORD). Just stick with your code I think.

Cheers,
Jarrod

I Just thought
MyNo := (LDWord * $FFFFFFFF) + SDWord;

But I like your packed record idea - that is good;

This has been mentioned here for the case where two single variables are the same size … Delphi 'absolute'

But it works equally for your case :

{$APPTYPE CONSOLE}
program Project1;

uses
  System.Types, System.SysUtils;
type
   DW_tuple = array[0..1] of UInt32;


function To_DWords( u:UInt64 ) : DW_tuple;  inline;
var
   dd : DW_tuple absolute u;
begin
   exit( dd );
end;


function To_UInt64( dd:DW_tuple ) : UInt64;  inline;
var
   u : UInt64 absolute dd;
begin
   exit( u );
end;


const
      u  : UInt64   = $FEDC_BA98_1234_5678;
      dd : DW_tuple = ($1111,$2222);

begin
  var d2 := To_DWords(u);
      writeln('0x', u.ToHexString);
      writeln('Ox', d2[0].ToHexString, '  ', 'Ox', d2[1].ToHexString);
      writeln;

  var u2 := To_UInt64(dd);
      writeln('Ox', dd[0].ToHexString, '  ', 'Ox', dd[1].ToHexString);
      writeln('0x', u2.ToHexString);
      writeln;

      readln;
end.

BUT as you can see, just interpreting the same memory leads to rearrangements that you might not expect in the interpretation of each data type.

Or you can use a variant record, like @Jarrod said.

{$APPTYPE CONSOLE}
program Project2;

uses
  System.SysUtils;

type
  DW_tuple = array[0..1] of UInt32;

  MyData = (_uint64, _dwords);

  data = record
           case MyData of
              _uint64 : ( u : UInt64  );
              _dwords : (dd : DW_tuple);
           end;

const
      u  : UInt64   = $FEDC_BA98_1234_5678;
      dd : DW_tuple = ($1111,$2222);
var
      x1, x2 : data;

begin
      x1.u := u;
      writeln('0x', x1.u.ToHexString);
      writeln('Ox', x1.dd[0].ToHexString, '  ', 'Ox', x1.dd[1].ToHexString);
      writeln;

      x2.dd := dd;
      writeln('0x', x2.u.ToHexString);
      writeln('Ox', x2.dd[0].ToHexString, '  ', 'Ox', x2.dd[1].ToHexString);
      writeln;

      readln;
end.

You still get the ‘rearrangement’ effect.

Ok. Hilarious and sad.

It has only just occurred to me that the first example is just the longest possible way of writing a hard cast. :disappointed_relieved::sweat_smile:

{$APPTYPE CONSOLE}
program Project3;

uses
      System.SysUtils;
type
      DW_tuple = array[0..1] of UInt32;

const
      u  : UInt64   = $FEDC_BA98_1234_5678;
      dd : DW_tuple = ($1111,$2222);

begin
  var d2 := DW_tuple(u);
      writeln('0x', u.ToHexString);
      writeln('Ox', d2[0].ToHexString, '  ', 'Ox', d2[1].ToHexString);
      writeln;

  var u2 := UInt64(dd);
      writeln('Ox', dd[0].ToHexString, '  ', 'Ox', dd[1].ToHexString);
      writeln('0x', u2.ToHexString);
      writeln;

      readln;
end.

But maybe sometimes more explicit is more betterer. (?) ¯\(ツ)

1 Like

Yeah the ‘rearragement’ is due to the value being stored in memory in little endian format:

Value: 0x0123456789ABCDEF
Bits:    ^high       low^
Memory address: 0   1   2   3   4   5   6   7
Byte value:     EF  CD  AB  89  67  45  23  01

When we use the above value we are working with variables/registers with bits represented highest to lowest, the same as the way that the value is written which is intuitive. Doing shr 32 (shift right) moves the 01234567 part in the high bits down to the low bits (and zeroes the high bits) and the final value is 0x0000000001234567.

But when the value, represented as say UInt64, is stored in memory, the byte order is reversed as EFCD… This little endian format is used with most modern CPUs and applies to all integral types, not just UInt64. So when you overlay 2 x DWORD on that memory the first DWORD (4 bytes) is the low 4 bytes/32 bits of the value and second DWORD is the high 4 bytes/32 bits of the value, resulting in the unexpected results.

Use System.SysUtils.Int64Rec

3 Likes

Just a nitpick on terms because I saw some people mention “rotate” - shr or shl is not rotate, it is shift. There are also rotate instructions in asm but unfortunately we don’t have them in the pascal language.

1 Like

Yeah, knew that. Didn’t want to confuse things given the original said rotate :smiley:. It is odd that we don’t have rotate equivalents. Other than ASM it can be done with two shifts and an or but still odd it’s not there.

The problem for us learners is to realize the flexibility of using Delphi
I never knew an array is also a packed record

But the reality is

const
      u  : UInt64   = $FEDC_BA98_1234_5678

begin
   Lower :=  U and $FFFF_FFFF;
   Upper := U and not $FFFF_FFFF;
   Upper := Upper div $FFFF_FFFF;

So why have a record or anything
and UInt64 is a stupid name to use where NNo64 makes sense as natural number is what it is and we do not need to follow ‘C’ with special complicated terms to remember

1 Like

You gotta be kidding :slightly_smiling_face:

UInt64 and UInt32 are the best possible names.

Unsigned & Integer & bitwidth … it’s all there, right on the packet.

Cardinal is historic and cute, but hardly informative.

And some of the other ones change size when you use the different compilers.

2 Likes

Cardinal is a long way from a natural number that original Delphi used wrongly
Unsigned & Integer & bitwidth are correct terms but are wordy to be writing all the time
many languages Integer is Integer is targeted at 32 bit and the new languages
use Int to not be wordy
And Delphi has always used Int64 that is Bitwidth
As languages drive to not be wordy they default with Var already set after the procedure statement or header statement - so you don’t have to write it.

Do a structured modern approach to Delphi 1/ simplify 2/ non wordy, 3/ still backward compatible is not stupid thinking at all.

Like Properties Private, Protected, Public, Published is a good example of simplify

I often start my page to include
Type
NNo64: …
NNo32: …
NNo16: Word;
Int32: integer;
just to simplify so I’m reterming things

Wordy? I don’t see it.

Int32 Int64 is signed.

UInt32 UInt64 is unsigned.

‘Natural number’ is Mathematics more than Computer Science.

BTW, ‘Natural number’ is equal in the field of mathematics on a level with ‘FreeAndNil’ as to whether it includes zero (ie 0,1,2 3,…) or not (ie 1,2 3,…) :joy:

PS : Of course it includes zero!

1 Like

At the end it is opinion
Their is so many things to like about Delphi as every one can pick improvements
Is objects within objects and the main object can do

Properties ColCount: Integer read SubObject.Count write SetColCount;

PS : Of course it includes zero!
and we have that and that is good

1 Like