I cannot use SHL (shift left) with int64 variables
I'd avoid all arithmetic (your code has additions and shifts) and do it like this:
Int64Rec(Result).Bytes[0] := b8;
Int64Rec(Result).Bytes[1] := b7;
//etc.
Int64Rec
is defined in SysUtils like this:
Int64Rec = packed record
case Integer of
0: (Lo, Hi: Cardinal);
1: (Cardinals: array [0..1] of Cardinal);
2: (Words: array [0..3] of Word);
3: (Bytes: array [0..7] of Byte);
end;
If you stored your bytes in an array then you could wrap it all up in a for
loop.
I'm assuming that your Result
is Int64
already, and b8
, b7
and so on are declared as Byte
. The compiler needs a little help. Typecast to Int64
:
Result := b8;
Result := Result + (b7 shl 8);
Result := Result + (b6 shl 16);
Result := Result + (b5 shl 24);
Result := Result + (Int64(b4) shl 32);
Result := Result + (Int64(b3) shl 40);
Result := Result + (Int64(b2) shl 48);
Result := Result + (Int64(msb) shl 56);
Here is another solution that uses shifts but I believe is "cleaner" (though not as clean as David's suggestion):
result := MSB;
result := (result shl 8) or b2; { could use "shl sizeof(b2)" instead of 8 }
result := (result shl 8) or b3;
etc
result := (result shl 8) or b8;
this solution avoids all the "magic" shifting values and it is probably easier to understand. Also, if MSB..b8 (or b1..b8) were an array of bytes, the above code could easily be converted into a single line loop.
To answer your question as to why the compiler does not accept the value 40 and above, the reason is most likely due to this quote from Volume 2B of the Intel Instruction Set Reference for the SHLD instruction:
In non-64-bit modes and default 64-bit mode; only bits 0 through 4 of the count are used. This masks the count to a value between 0 and 31. If a count is greater than the operand size, the result is undefined.
The equivalent condition on the SHL instruction is not quite as stringent:
The 8086 does not mask the shift count. However, all other IA-32 processors (starting with the Intel 286 processor) do mask the shift count to 5 bits, resulting in a maximum count of 31. This masking is done in all operating modes (including the virtual-8086 mode) to reduce the maximum execution time of the instructions.
In either case, a value greater than 31 is either useless (when using SHL) or undefined (when using SHLD). The compiler obviously knows this and, it is preventing you from writing code that is potentially erroneous (in 64 bit mode.)
if you, indeed are doing this operation 50 BILLION times, then you may want to consider doing it in inline assembly, which would be quite simple:
asm
xor eax, eax
or eax, MSB { or b1 depending on how you named the MSB }
shl eax, 8
or eax, b2
shl eax, 8
or eax, b3
shl eax, 8
or eax, b4
mov High32bit, eax
end;
and repeat the above operation for the low 32 bit dword and b5 thru b8. I didn't suggest using SHLD because I don't believe Delphi supports the 64 bit registers nor 64bit instructions (I could be wrong, I've never tried it.)
Note: I've heard rumors that the 64bit version of Delphi will not support inline assembly. This may or may not be the case but, I would stay away from inline assembly unless it is truly absolutely necessary.
Hope that helps,
John.
PS: there is another reason why David Heffernan's solution is the best one. In the solution I presented, every instruction depends on the previous instruction (that is, eax must be shifted by 8 before the next "or" instruction can be executed). David's solution sets individual bytes, as such, each assignment is independent of the previous assignment, this will allow the processor to potentially carry out multiple assignments in parallel. In this era of multi-core processors this has the potential of being quite a bit faster than the assembly code I gave as an example.