1

I'm trying to convert an old pascal procedure from Delphi 6 into Delphi 11.2 Alexandria.

I have the below function which fills in the modulo table with multiples of A, modulo being an Array [0..255, 0..19] of Byte.

procedure TForm.SetModulo(A: TCode128);
var
  I, J, Remainder : Byte;    

  NPointerStart: PCardinal;
  NPointer: PCardinal;
  ModuloPointer: PCardinal;
  WordPointer: PWord;

begin
  NPointerStart:= Addr(A);
  for I:= 0 to 255 do begin
    NPointer:= Pointer(NPointerStart);
    Remainder:= 0;
    ModuloPointer:= Addr(Modulo[I][0]);
    for J:= 0 to 3 do begin
      ModuloPointer^:= ((NPointer^ * I) + Remainder);  //<- Int Overflow error

      WordPointer:= Pointer(NPointer);
      Remainder:= ((WordPointer^ * I) + Remainder) div $10000;             
      Inc(WordPointer);
      Remainder:= ((WordPointer^ * I) + Remainder) div $10000;              

      Inc(NPointer);
      Inc(ModuloPointer);
    end;
    ModuloPointer^:= Remainder;
  end;
end;

The procedure parameter TCode128 is Array[0..15] of Byte and is sent byte addresses ($12, $C4.......).

In delphi 11.2 I get an Integer Overflow error at ModuloPointer^:= ((NPointer^ * I) + Remainder); The value of I is just 2 when it throws the error.

When I run the same procedure on Delphi 10.4.2 there is no integer overflow error and everything works just fine.

I genuinely can not think of what's going wrong here, I tried typecasting ModuloPointer, NPointer to Uint64and Modulo to Word but no go(probably makes no sense).

Am I missing something fairly simple or there have been some changes in the Delphi 11.2 IDE? Either ways how do we get around this and what's going wrong?

7
  • NPointer points to a Cardinal, which is 4 bytes. So NPointer^ is the 32-bit integer starting at NPointer. You then double it, which may indeed overflow. Commented Jan 5, 2023 at 9:43
  • 1
    Your compiler options have changed probably. You need to disable overflow checking for this code to work. You absolutely should not change the types at random as you've been trying. That way leads to madness. Use {$Q-} to disable overflow checking for this code. I wrap up such code in {$Q-}...{$IFDEF OVERFLOWCHECKSON}{$Q+}{$ENDIF} where OVERFLOWCHECKSON is defined in my common include file. Commented Jan 5, 2023 at 9:50
  • Check that the compiler directives are identical in both 10.4.2 and 11.2 (press Ctrl+O Ctrl+O to copy the current settings to the top of the current file, then cut/paste it to NotePad, then do the same in the other version of Delphi and compare the two sets of directives - especially the $Q directive, which controls Overflow Exception). Also - what is the value of NPointer^and I when it overflows - in both 10.4.2 and 11.2? Commented Jan 5, 2023 at 9:51
  • Check your Project Options to see if Owerflow Checking is even enabled in your project. Also you might consider refactoring your code to not rely on pointer math otherwise you will run into problems when migrating to 64 bit architecture due to changes in pointer size. Commented Jan 5, 2023 at 9:53
  • 2
    ASLR? If so "When I run the same procedure on Delphi 10.4.2 there is no integer overflow error and everything works just fine." is incorrect and should be "When I run the same procedure on Delphi 10.4.2 there is often no integer overflow error and everything works just fine by accident." Commented Jan 5, 2023 at 10:36

1 Answer 1

4

An equivalent of your pointer juggling would be:

type
  TCode128= packed record
    case Byte of  // The same length for all variants, equal to UNIONs in C
    1: ( b: Array[0.. 15] of Byte );  // Accessing as 16 bytes
    2: ( c: Array[0.. 3] of Cardinal );  // As 4 DWORDs
    3: ( w: Array[0.. 7] of Word );  // As 8 WORDs
  end;

var
  Modulo: Array[0.. 255] of Array[0.. 4] of Cardinal;  // DWORDs where we need them

procedure SetModulo( A: TCode128 );
var
  I, J, Remainder: Byte;
begin
  for I:= 0 to 255 do begin  // For each "modulo"
    Remainder:= 0;
    for J:= 0 to 3 do begin  // Each first 4 remainders
      // Access and store as DWORDs right away
      // Allowing a higher product/sum than CARDINAL could hold, but only store 32 bit
      Modulo[I][J]:= (Int64(A.c[J])* I+ Remainder) and $FFFFFFFF;

      Remainder:= (A.w[J* 2]* I+ Remainder) div $10000;  // Access as WORDs
      Remainder:= (A.w[J* 2+ 1]* I+ Remainder) div $10000;
    end;
    Modulo[I][4]:= Remainder;  // Last 5th remainder
  end;
end;

As you can see this is logically better understandable, needs much fewer variables, and is platform independant. The TCode128 definition has 3 variables, but they all point to the same memory - this way you can define that memory in multiple ways to later access it much more brain and data type friendly.

Multiplying a Cardinal by 255 and adding at max 255 again is prone to be an overflow, because it would only work for values up to 16843008 ($1010100). That's why I pick only the (least) 32 bit of that product/sum.

Sign up to request clarification or add additional context in comments.

1 Comment

I really like this approach and the use of case for varying data type, rather than refactoring you made it easier to grasp(and maybe refactored too).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.