Older Delphi code sometimes included a small _ftol helper to convert a floating-point value to an integer. In 32-bit projects it was common to write this kind of helper with inline assembler, especially in graphics and PDF code that had been tuned for older compilers.
|
1 2 3 4 5 6 7 8 |
function _ftol( f: double) : Integer; cdecl; begin asm lea eax, f fstp qword ptr [eax] end; result := Trunc(f); end; |
That approach breaks when the same unit is compiled for a 64-bit target. The Delphi 64-bit compiler reports E1025 Unsupported language feature: 'ASM' because DCC64 does not support mixed Pascal and inline assembly inside normal Pascal routines.
Why the 64-bit compiler rejects it
DCC64 can assemble 64-bit assembly routines, but the routine must be written as a complete assembler routine with the 64-bit calling convention in mind. You cannot copy 32-bit inline assembler into a Pascal function body and expect the compiler to translate register usage, stack layout, or x87 behavior.
A full assembler version is possible, and code like the following shows the kind of control-word handling that may be required when the value is already on the FPU stack.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function _ftol: Integer; cdecl; // Assumes double value is in FPU stack on entry // Make a truncation to integer and put it into function result var TmpVal: Int64; SaveCW, ScratchCW: word; asm .NOFRAME fnstcw word ptr [SaveCW] fnstcw word ptr [ScratchCW] or word ptr [ScratchCW], 0F00h ;// trunc toward zero, full precision fldcw word ptr [ScratchCW] fistp qword ptr [TmpVal] fldcw word ptr [SaveCW] mov rax, TmpVal end; |
Prefer Pascal when the goal is only truncation
For most application code, the assembler routine is unnecessary. If the real requirement is to truncate a Double toward zero and return an integer value, the clear and portable version is simply:
|
1 2 3 4 |
function _ftol(f: double): integer; cdecl; begin Result := Trunc(f); end; |
Trunc expresses the intent directly, compiles for both 32-bit and 64-bit targets, and avoids the maintenance cost of architecture-specific assembler. It is also easier for future maintainers to reason about range checking and overflow behavior.
Migration guidance
- Use the Pascal implementation when you control the call site and only need numeric conversion.
- Keep an assembler export only when binary compatibility requires a specific symbol and calling convention.
- Do not copy 32-bit register code into a 64-bit routine without reviewing the calling convention and data width.
- Test edge cases such as negative values, large values, and values outside the target integer range.
Range and behavior considerations
Replacing assembler with Trunc should still be treated as a behavior change that deserves tests. Confirm how your code should behave for negative numbers, fractions near an integer boundary, and values outside the range of Integer. The old assembler may have depended on FPU control-word state, while the Pascal version follows the RTL implementation for the target compiler.
If the helper sits inside a PDF or graphics component, test output that uses rotated text, scaled coordinates, and transformed drawing commands. Those areas are more likely to reveal subtle float-to-integer conversion differences than simple examples.
Recommended migration path
First, replace private conversion helpers with clear Pascal code where possible. Second, keep the original 32-bit implementation only behind a 32-bit conditional if it is still required. Third, add a separate 64-bit branch only when a measured performance or binary compatibility requirement justifies it. This keeps the portable path simple and makes the architecture-specific path an exception rather than the default.