DWord function arg is overwritten with record arg address

General discussion on mikroPascal PRO for dsPIC30/33 and PIC24.
Post Reply
Author
Message
VCC
Posts: 463
Joined: 08 Jun 2009 18:31
Location: Romania

DWord function arg is overwritten with record arg address

#1 Post by VCC » 27 Apr 2024 07:35

Hi,
here is an example of function call with two (passed by ref) record parameters and two DWord parameters. The first DWord parameter is partially overwritten with the value of the previous record parameter (which stores an address). The code is a small part of the DynArrays library: https://libstock.mikroe.com/projects/vi ... /dynarrays

See the header of CopyFromDynArray function, where the high word of AIndex is overwritten with the value of ASrcArr (which is a pointer in this case, since the arg is passed by ref):

Code: Select all

function CopyFromDynArray(var ADestArr, ASrcArr: TDynArrayOfByte; {$IFDEF AppArch16} ADummy, {$ENDIF} AIndex, ACount: TDynArrayLength): Boolean;
The ADummy parameter is the workaround, which is enabled by defining AppArch16 compiler directive. When enabled, this one does not "take the hit" anymore, because the parameters are passed differently onto the stack.

Code: Select all

//Test code for simulator.

program SecondTestVarParam;

const
  CMaxDynArrayLength = 65536;  //bytes
  
  HEAP_START: DWord = $001600;  //make sure this is a valid address and is DWord aligned !
  HEAP_SIZE: DWord = 7000;   //Make sure enough memory is allocated. See the desktop application (simulator) for guidance.


type
  TDynArrayLength = DWord;
  PByte = ^far const code Byte;
  PIntPtr = PByte;
  
  TDynArrayOfByteContent = array[0..CMaxDynArrayLength - 1] of Byte;
  PDynArrayOfByteContent = ^TDynArrayOfByteContent;

  TDynArrayOfByte = record
    Len: TDynArrayLength;
    Content: PDynArrayOfByteContent;
    //{$IFDEF IsDesktop}
    //  Initialized: string;
    //{$ENDIF}
  end;
  
  
//{$DEFINE AppArch16}   ////////////////////////////// define this, to add an extra argument to some of the following functions, which will get overwritten


function Min32(a, b: LongInt): LongInt;
begin
  if a < b then
    Result := a
  else
    Result := b;
end;


procedure InitDynArrayToEmpty(var AArr: TDynArrayOfByte); //do not call this on an array, which is already allocated, because it results in memory leaks
begin
  AArr.Len := 0;       //this is required when allocating a new array
  AArr.Content := nil; //probably, not needed, since Len is set to 0

  {$IFDEF IsDesktop}
    AArr.Initialized := 'init';  //some string, different than ''
  {$ENDIF}
end;


function SetDynLength(var AArr: TDynArrayOfByte; ANewLength: TDynArrayLength): Boolean; //returns True if successful, or False if it can't allocate memory
var
  OldPointer: {$IFDEF IsDesktop} PIntPtr; {$ELSE} DWord; {$ENDIF}
begin
  {$IFDEF IsDesktop}
    CheckInitializedDynArray(AArr);
  {$ENDIF}

  Result := True;

  if ANewLength = 0 then
  begin
    if AArr.Len > 0 then
    begin
      {$IFnDEF IsDesktop}
        {$IFDEF RoundAlloc}FreeMemRound{$ELSE}FreeMem{$ENDIF}(AArr.Content, AArr.Len);
      {$ELSE}
        {$IFDEF UsingDynTFT}
          FreeMem(TPtrRec(AArr.Content), AArr.Len);
        {$ELSE}
          FreeMem(AArr.Content, AArr.Len);
        {$ENDIF}
      {$ENDIF}
    end;

    AArr.Len := 0;
    Exit;
  end;

  if AArr.Len = 0 then
    OldPointer := nil
  else
    OldPointer := PIntPtr(AArr.Content);

  if (OldPointer = nil) and (AArr.Len > 0) then
    Exit;

  {$IFnDEF IsDesktop}
    {$IFDEF RoundAlloc}GetMemRound{$ELSE}GetMem{$ENDIF}(AArr.Content, ANewLength);
    if MM_error or (AArr.Content = nil) then    //If compilers report that "MM_error" was not declared, then open MemManager library and uncomment MM_error header.
    begin
      Result := False;
      Exit;
    end;

    if OldPointer <> nil then
      {$IFDEF RedefineMemMove} MemMove32 {$ELSE} MemMove {$ENDIF}(AArr.Content, OldPointer, Min32(ANewLength, AArr.Len));   //OldPointer = src, AArr.Content = dest
  {$ELSE}
    try
      {$IFDEF UsingDynTFT}
        GetMem(TPtrRec(AArr.Content), ANewLength);
        if MM_error or (AArr.Content = nil) then
        begin
          Result := False;
          Exit;
        end;
      {$ELSE}
        GetMem(AArr.Content, ANewLength);
      {$ENDIF}

      //AArr.Len is still the old array length. Only the Content field points somewhere else.

      if OldPointer <> nil then
        MemMove(AArr.Content, OldPointer, Min32(ANewLength, AArr.Len))   // the rest of the content is not initialized
    except
      Result := False;
    end;
  {$ENDIF}

  if AArr.Len > 0 then
  begin
    {$IFnDEF IsDesktop}
      {$IFDEF RoundAlloc}FreeMemRound{$ELSE}FreeMem{$ENDIF}(OldPointer, AArr.Len);
    {$ELSE}
      {$IFDEF UsingDynTFT}
        FreeMem(TPtrRec(OldPointer), AArr.Len);
      {$ELSE}
        FreeMem(OldPointer, AArr.Len);
      {$ENDIF}
    {$ENDIF}
  end;

  AArr.Len := ANewLength;
end;


procedure FreeDynArray(var AArr: TDynArrayOfByte);
begin
  {$IFDEF IsDesktop}
    CheckInitializedDynArray(AArr);
  {$ENDIF}

  SetDynLength(AArr, 0);
end;
  

function CopyFromDynArray(var ADestArr, ASrcArr: TDynArrayOfByte; {$IFDEF AppArch16} ADummy, {$ENDIF} AIndex, ACount: TDynArrayLength): Boolean;
var
  OldPointer: {$IFDEF IsDesktop} PIntPtr; {$ELSE} DWord; {$ENDIF}
begin
  Result := True;
  InitDynArrayToEmpty(ADestArr);

  if ACount = 0 then
    Exit;

  if ASrcArr.Len = 0 then
  begin
    Result := False;
    Exit;
  end;

  LATA := AIndex shr 16;   //get the high word. /////////// The high word of AIndex is overwritten by the address of Arr, which is a global var.
  LATB := AIndex;          ///////////////////////////////  AIndex should be 1, but the high word of AIndex is overwritten. !!!!!!!!!!!!!!
  LATC := ACount shr 16;  //============================= The high word of ACount is properly set to 0.
  LATD := ACount;         //============================= ACount should be 3.

  if AIndex > ASrcArr.Len - 1 then
  begin
    Result := False;
    Exit;
  end;

  if ACount > ASrcArr.Len - AIndex then
    ACount := ASrcArr.Len - AIndex;

  if not SetDynLength(ADestArr, ACount) then
  begin
    Result := False;
    Exit;
  end;

  {$IFnDEF IsDesktop}
    OldPointer := DWord(ASrcArr.Content) + AIndex;
  {$ELSE}
    OldPointer := Pointer(PtrUInt(ASrcArr.Content) + PtrUInt(AIndex));
  {$ENDIF}
  {$IFDEF RedefineMemMove} MemMove32 {$ELSE} MemMove {$ENDIF}(ADestArr.Content, OldPointer, ACount);
end;


function RemoveStartBytesFromDynArray(ACount: TDynArrayLength; var AArr: TDynArrayOfByte): Boolean;
var
  PartialArr: TDynArrayOfByte;
  NewLen: TDynArrayLength;
  {$IFDEF AppArch16} Dummy: TDynArrayLength; {$ENDIF}
begin
  Result := True;

  if ACount >= AArr.Len then
  begin
    FreeDynArray(AArr);
    Exit;
  end;

  if ACount = 0 then
    Exit;

  InitDynArrayToEmpty(PartialArr);
  NewLen := AArr.Len - ACount;
  
  {$IFDEF AppArch16}
    Dummy := 0;
  {$ENDIF}

  if not CopyFromDynArray(PartialArr, AArr, {$IFDEF AppArch16} Dummy, {$ENDIF} ACount, NewLen) then  //fixed by adding Dummy
  begin
    Result := False;
    Exit;
  end;

  {$IFDEF RedefineMemMove} MemMove32 {$ELSE} MemMove {$ENDIF}(AArr.Content, PartialArr.Content, NewLen);
  FreeDynArray(PartialArr);

  Result := SetDynLength(AArr, NewLen);
end;
  
  
var
  Arr: TDynArrayOfByte;
  TempCount: DWord;

begin
  MM_Init;
  TempCount := 0;
  TempCount := TempCount + 3;
    
  InitDynArrayToEmpty(Arr);
  SetDynLength(Arr, 4);
  RemoveStartBytesFromDynArray(1, Arr);  //remove one item from the beginning (i.e., the item at index 0)
  
  LATF := Arr.Len;    //this should be 3, not 4
  
  repeat
  until False;
end.
Compiler mikroPascal PRO for dsPIC v7.1.0.
MCU: dsPIC33EP512MU810.
Bug_dsPIC_var_param.png
Bug_dsPIC_var_param.png (95.68 KiB) Viewed 76 times
Thank you :)
Attachments
SecondTestVarParam.zip
(103.02 KiB) Downloaded 6 times

Post Reply

Return to “mikroPascal PRO for dsPIC30/33 and PIC24 General”