//This file was generated by the LASAL2 CodeGenerator  -- 
//Please, do not edit this file (it might be overwritten by the next generator run)
//{{LSL_DECLARATION

(*!
<Class
	Name               = "RAMex"
	Revision           = "1.17"
	GUID               = "{2E19877D-812C-4F76-81E3-C3507CBD2152}"
	RealtimeTask       = "false"
	CyclicTask         = "false"
	BackgroundTask     = "false"
	Sigmatek           = "true"
	OSInterface        = "false"
	HighPriority       = "false"
	Automatic          = "false"
	UpdateMode         = "Prescan"
	IconPath           = ".\Class\RAMex\Ramex.ICO"
	SharedCommandTable = "true"
	Objectsize         = "(284,120)"
	Comment            = "storage of retentive data with arbitrary length&#13;&#10;!! If the RamEx is using file storage please note the method UpdateToFile !!">
	<Channels>
		<Server Name="m_udLength" WriteProtected="false">
		</Server>
		<Client Name="MultiTask" Required="false" Internal="false" Comment="Objectchannel to _Multitast &#13;&#10;(Connection is made automatically)"/>
		<Client Name="UseFile" Required="false" Internal="false" Comment="Initialize this client with 1 to write the retentive data to a file!&#13;&#10;0 = default!"/>
	</Channels>
	<RevDoku>
		<Owner/>
		<Dokumentation Revision="1.17" Date="2017-01-23" Author="praerw" Company="sigmatek" Description="Added new ITASK calls to inform the os when data that should be written to a file is pending. The os uses this information to delay a reboot until all data is written to the file."/>
		<Dokumentation Revision="1.16" Date="2014-03-14" Author="kolott" Company="sigmatek" Description="client threadsafe at base added"/>
		<Dokumentation Revision="1.15" Date="2013-11-06" Author="feshar" Company="sigmatek" Description="Set a new RamEx object to length 0.&#13;&#10;New function to check async activity."/>
		<Dokumentation Revision="1.14" Date="2013-10-03" Author="feshar" Company="sigmatek" Description="Bugfix when converting RamEx SRAM data to file by using SRAMFORMAT 2.&#13;&#10;For this bugfix the Loader 02.02.139 is necessary."/>
		<Dokumentation Revision="1.13" Date="2013-09-04" Author="kolott" Company="sigmatek" Description="Bugfix RuntimeWatchdog II"/>
		<Dokumentation Revision="1.12" Date="2013-08-23" Author="kolott" Company="sigmatek" Description="Bugfix RuntimeWatchdog"/>
		<Dokumentation Revision="1.11" Date="2013-04-08" Author="kolott" Company="sigmatek" Description="ARM-Ready"/>
		<Dokumentation Revision="1.10" Date="2013-03-21" Author="spimar" Company="Sigmatek" Description="New revision number for correct useage with loader;"/>
		<Dokumentation Revision="2.10" Date="2013-02-21" Author="spimar" Company="Sigmatek" Description="New NewInst for RamImage tool if RamEx objects are using file storage."/>
		<Dokumentation Revision="1.9" Date="2012-09-21" Author="feshar" Company="Sigmatek" Description="String Initvalue was not set for RamEx objects with file storage."/>
		<Dokumentation Revision="1.8" Date="2012-09-19" Author="feshar" Company="Sigmatek" Description="If a new RamEx object is using file storage, a not initalized pointer is used."/>
		<Dokumentation Revision="1.7" Date="2012-09-19" Author="spimar" Company="Sigmatek" Description="Corrected a define;"/>
		<Dokumentation Revision="1.6" Date="2012-05-03" Author="feshar" Company="Sigmatek" Description="RamEx can use file storage now. (Set The client UseFile to &apos;1&apos;)&#13;&#10;!! If the RamEx is using file storage please note the method UpdateToFile !!"/>
		<Dokumentation Revision="1.5" Date="2012-02-07" Author="PraErw" Company="Sigmatek" Description="Change in RAMex::Alloc:&#13;&#10;Improved allocation of sram, unused memory after SetSize is freed.&#13;&#10;To benefit from these changes, loader version &gt;= 2.2.121 is required."/>
		<Dokumentation Revision="1.4" Date="2011-09-28" Author="SpiMar" Company="Sigmatek" Description="The input and output parameters of the server write methods  are now fitted to the datatype of the server;"/>
	</RevDoku>
	<Network Name="RAMex">
		<!-- List of Components in this network -->
		<Components>
			<Object
				Name       = "_base"
				GUID       = "{69FA3C23-B049-4F83-B4BC-D30F993C8C98}"
				Class      = "MerkerEx"
				Position   = "(90,150)"
				Visualized = "true">
				<Channels>
					<Server Name="m_udLength"/>
					<Client Name="ThreadSafe"/>
				</Channels>
			</Object>
		</Components>
		<Comments>
		</Comments>
		<!-- List of Connections in this network -->
		<Connections>
			<Connection Source="this.m_udLength" Destination="_base.m_udLength" Vertices="(562,210),(390,240),"/>
			<Connection Source="_base.ThreadSafe" Destination="this.ThreadSafe" Vertices="(90,240),(38,210),"/>
		</Connections>
		<!-- Headerfiles -->
		<Options>
		</Options>
	</Network>
</Class>
*)
#pragma using MerkerEx

RAMex : CLASS
: MerkerEx
  //Servers:
  //Clients:
	UseFile 	: CltCh_DINT;
	MultiTask 	: CltChCmd__MultiTask;
  //Variables:
		pRAMaddr 	: ^XRamCell;
  //Functions:
	
	FUNCTION RAMex
		VAR_OUTPUT
			ret_code 	: ConfStates;			//! <Variable Comment=" C_OK if memory could be allocated, C_OUTPF_NEAR if a failure occured" Name="RAMex.ret_code"/>
		END_VAR;
				//! <Function Comment="Use this function to change the size of the data buffer&#13;&#10;Diese Funktion kann die Größe des Datenpuffers ändern.&#13;&#10;" Name="SetSize"/>
	FUNCTION VIRTUAL GLOBAL SetSize
		VAR_INPUT
			udSize 	: UDINT;			//! <Variable Comment="  neue Größe, 0 gibt den Puffer frei" Name="SetSize.udSize"/>
		END_VAR
		VAR_OUTPUT
			ret_code 	: ConfStates;
		END_VAR;
				//! <Function Comment=" Use this function to change a part of the data buffer.&#13;&#10; Call SetSize before, to make sure, the internal data buffer is&#13;&#10; sufficiently big.&#13;&#10;Mit dieser Funktion kann ein Teil des internen Datenpuffers&#13;&#10;beschrieben werden. Um sicherzustellen, daß ausreichend Speicher&#13;&#10;zur Verfügung steht, muß vorher ggf. SetSize() aufgerufen werden.&#13;&#10;" Name="SetDataAt"/>
	FUNCTION VIRTUAL GLOBAL SetDataAt
		VAR_INPUT
			pData 	: ^USINT;			//! <Variable Comment="pointer to a new block of memory" Name="SetDataAt.pData"/>
			udSize 	: UDINT;			//! <Variable Comment=" Anzahl der zu kopierenden Bytes" Name="SetDataAt.udSize"/>
			udAt 	: UDINT;			//! <Variable Comment=" start copy at this offset in the internal buffer" Name="SetDataAt.udAt"/>
		END_VAR
		VAR_OUTPUT
			ret_code 	: ConfStates;
		END_VAR;
				//! <Function Comment=" Mit dieser Funktion kann der gesamte interne Datenpuffer des&#13;&#10; Objekts gesetzt werden. Die Funktion erwartet sich einen Zeiger auf&#13;&#10; einen Datenpuffer mit der Größe des internen Puffers. Exakt diese&#13;&#10; Datenmenge wird dann nämlich auf den internen Puffer umkopiert.&#13;&#10; this is the function that enables the user to&#13;&#10; set all the data of the data buffer. The function expects a&#13;&#10; pointer to a data buffer with at least the size&#13;&#10; of the internal data buffer, exactly the size of the internal&#13;&#10; buffer is copied from the source&#13;&#10;" Name="SetData"/>
	FUNCTION VIRTUAL GLOBAL SetData
		VAR_INPUT
			pData 	: ^USINT;
		END_VAR;
				//! <Function Comment="Diese Funktion kann die Größe des Datenpuffers ändern.&#13;&#10; The function is called to resize the data buffer of an object.&#13;&#10;" Name="Alloc"/>
	FUNCTION VIRTUAL GLOBAL Alloc
		VAR_INPUT
			udSize 	: UDINT;			//! <Variable Comment="  new size, 0 frees the buffer" Name="Alloc.udSize"/>
		END_VAR
		VAR_OUTPUT
			pCode 	: ^void;			//! <Variable Comment="pointer to allocated block of memory 0= ko memory available" Name="Alloc.pCode"/>
		END_VAR;
				//! <Function Comment="This function is only necessary, if the RamEx is using file storage.&#13;&#10;It has to be called after manipulating data, if the data has been changed by using a&#13;&#10;pointer to the data (GetDataPtr).&#13;&#10;" Name="UpdateToFile"/>
	FUNCTION VIRTUAL GLOBAL UpdateToFile;
	
	FUNCTION CreateFileName
		VAR_INPUT
			pFileName 	: ^USINT;
		END_VAR
		VAR_OUTPUT
			crc 	: UDINT;
		END_VAR;
	
	FUNCTION GetObjectCRC
		VAR_OUTPUT
			crc 	: UDINT;
		END_VAR;
	
	FUNCTION RamExToFile
		VAR_OUTPUT
			ret_code 	: ConfStates;
		END_VAR;
	
	FUNCTION AllocToFile
		VAR_INPUT
			udSize 	: UDINT;
		END_VAR
		VAR_OUTPUT
			pCode 	: ^void;
		END_VAR;
	
	FUNCTION CreateFile
		VAR_INPUT
			saveFromSRAM 	: BOOL;
		END_VAR
		VAR_OUTPUT
			ret_code 	: BOOL;
		END_VAR;
	
	FUNCTION LoadFile
		VAR_INPUT
			loadToSRAM 	: BOOL;
		END_VAR
		VAR_OUTPUT
			ret_code 	: BOOL;
		END_VAR;
	
	FUNCTION SetDataAtToFile
		VAR_INPUT
			udSize 	: UDINT;
			udAt 	: UDINT;
		END_VAR
		VAR_OUTPUT
			ret_code 	: BOOL;
		END_VAR;
	
	FUNCTION SetSizeToFile
		VAR_INPUT
			udSize 	: UDINT;
		END_VAR
		VAR_OUTPUT
			ret_code 	: ConfStates;
		END_VAR;
	
	FUNCTION GLOBAL IsAsyncFileOperationInProgress
		VAR_OUTPUT
			state 	: UDINT;
		END_VAR;
	
	FUNCTION GLOBAL GetAsyncThreadHandle
		VAR_OUTPUT
			hThread 	: MT_TASKHANDLE;
		END_VAR;
				//! <Function Comment=" Handles a message, that is sent by the loader, when the&#13;&#10; application is running on a x386, and if there is retentive&#13;&#10; data, that belonged to the same object during the last run&#13;&#10; of the application.&#13;&#10; The object frees its currently allocated data and uses the&#13;&#10; retentive data instead.&#13;&#10;" Name="m_udLength.NewInst"/>
	FUNCTION VIRTUAL GLOBAL m_udLength::NewInst
		VAR_INPUT
			pPara 	: ^CmdStruct;
			pResult 	: ^results;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL m_udLength::Read
		VAR_OUTPUT
			output (EAX) 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL m_udLength::Write
		VAR_INPUT
			input (EAX) 	: UDINT;
		END_VAR
		VAR_OUTPUT
			result (EAX) 	: UDINT;
		END_VAR;
  //Tables:
	FUNCTION @STD
		VAR_OUTPUT
			ret_code	: CONFSTATES;
		END_VAR;
	FUNCTION GLOBAL TAB @CT_;
END_CLASS;

#pragma usingLtd _MultiTask


//}}LSL_DECLARATION


FUNCTION GLOBAL TAB RAMex::@CT_
0$UINT,
2#0100000000000000$UINT, //TY_RAMEX
1$UINT, 17$UINT, (SIZEOF(::RAMex))$UINT, 
0$UINT, 2$UINT, 0$UINT, 
TO_UDINT(933570811), "RAMex", //Class
TO_UDINT(2225119864), "MerkerEx", 1$UINT, 22$UINT, //Baseclass
//Servers:
//Clients:
(::RAMex.UseFile.pCh)$UINT, _CH_CLT_DATA$UINT, 2#0000000000000000$UINT, TO_UDINT(1841737910), "UseFile", 
(::RAMex.MultiTask.pCh)$UINT, _CH_CLT_OBJ$UINT, 2#0000000000000000$UINT, TO_UDINT(3505031941), "MultiTask", TO_UDINT(2641766936), "_MultiTask", 1$UINT, 1$UINT, 
END_FUNCTION


#define USER_CNT_RAMex 15

TYPE
	_LSL_STD_VMETH	: STRUCT
			CmdTable	: CMDMETH;
			UserFcts	: ARRAY[0..USER_CNT_RAMex] OF ^Void;
	END_STRUCT;
END_TYPE

FUNCTION RAMex::@STD
	VAR_OUTPUT
		ret_code	: CONFSTATES;
	END_VAR
	VAR
		vmt	: _LSL_STD_VMETH;
		nCmdSize	: UINT;
	END_VAR

	ret_code	:= MerkerEx::@STD();
	IF ret_code <> C_OK THEN
		RETURN;
	END_IF;
	nCmdSize		:= MerkerEx::m_udLength.pMeth^.nCmds$UINT*SIZEOF(pVoid) + CMDMETH.Init;

	_memcpy((#vmt.CmdTable)$^USINT, MerkerEx::m_udLength.pMeth, nCmdSize);
	vmt.CmdTable.nCmds		:= nSTDCMD + USER_CNT_RAMex;
	vmt.CmdTable.NewInstr		:= #m_udLength::NewInst();
	vmt.CmdTable.Read		:= #m_udLength::Read();
	vmt.CmdTable.Write		:= #m_udLength::Write();
#pragma warning (disable : 74)
	vmt.UserFcts[0]		:= #SetSize();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[1]		:= #SetDataAt();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[3]		:= #SetData();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[9]		:= #Alloc();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[10]		:= #UpdateToFile();

#pragma warning (default : 74)
	MerkerEx::m_udLength.pMeth		:= StoreCmd (pCmd := #vmt.CmdTable, SHARED);

	IF MerkerEx::m_udLength.pMeth THEN
		ret_code	:= C_OK;
	ELSE
		ret_code	:= C_OUTOF_NEAR;
		RETURN;
	END_IF;
	ret_code	:= RAMex();

END_FUNCTION

//{{LSL_IMPLEMENTATION

//#include <.\LSL_ST_LSLFILE.h>   // braucht man nichtmehr !!
#include <RTOS_MemIntern.h>
#include <LSL_ST_OSFILE.H>
#include <LSL_STITASK.H>
#include <rtos_privheader.h>

(******************************************************************************
*
*	Class RAMex
*	Created: Sigmatek
*	Changes:
*		1.2 => 1.3	/	15.12.2006	/	Mark:	//#SR001
*			don't reject pointer to SRAM when SetSize is 0 (only set m_udLength and used data in pRAMaddr^.udData to 0) 	
*		0.2 => 1.2	/	25.03.2005	/	Mark:	//#SR000
*			make compatible to lasal 2 with #define Lasal2_OBJ in "Memory.h"
*
******************************************************************************)

#define LASAL2_OBJ			//activate for Lasal2 Library

#define RAMEXFILES      "C:\lsldata\ramex"
#define FILEHEADERSIZE  8
#define RAMEXINDEXFILE  "C:\lsldata\ramex.idx"

#ifndef RAMEXMAILBOXSIZE
  #define RAMEXMAILBOXSIZE 2000
#endif

//---------------------------------------------
// im neuen ram die verbindung zum s-ram suchen  (Im Loader)
//---------------------------------------------  

//   Wird von allen S-Ram klassen verwendet um das objekt mit den
//   Daten zu verbinden
FUNCTION GLOBAL AWL Find_sRam
VAR_INPUT
	CRC32		: UDINT;
END_VAR
VAR_OUTPUT
	pRamAdr		: ^XramCell;
END_VAR;

FUNCTION GLOBAL AWL Find_sRamEx
VAR_INPUT
	CRC32		  : UDINT;
  bDontAdd  : BOOL;
END_VAR
VAR_OUTPUT
	pRamAdr		: ^XramCell;
END_VAR;

// neue funktion für den C-IPC
FUNCTION GLOBAL AWL Find_sRamFile
VAR_INPUT
	CRC32		:  UDINT;
	ppRamAdr	: ^UDINT;
	ppFileAdr	: ^XFileCell;
	uiType		: UDINT;				// 0=RamZelle 1=RamExZelle 	
END_VAR
VAR_OUTPUT
	state		: UDINT;
END_VAR;

// neue funktion für den C-IPC
FUNCTION GLOBAL AWL Find_sRamFileEx
VAR_INPUT
	CRC32		:  UDINT;
	ppRamAdr	: ^UDINT;
	ppFileAdr	: ^XFileCell;
	uiType		: UDINT;				// 0=RamZelle 1=RamExZelle 	
  bDontAdd  : BOOL;
END_VAR
VAR_OUTPUT
	state		: UDINT;
END_VAR;

// liefert S-RAM Version
FUNCTION GLOBAL AWL GetVersion_sRam
VAR_OUTPUT
	Version		: UDINT;
END_VAR;
// -- den end of memory pointer aktualisieren
//  ( nach jeder vergrösserung des speichers)
FUNCTION GLOBAL  GET_USED_S_RAM;


// -- Speicher mit der Lng length0 anfordern
FUNCTION GLOBAL SRAM_GetMemory
VAR_INPUT
   length0  : UDINT;
END_VAR
VAR_OUTPUT
   ret0 : ^USINT;
END_VAR;

FUNCTION GLOBAL SRAM_MarkMemory   // belegten speicher an alocation melden (beim hochfahren)
VAR_INPUT
   reladdr0 :^usint;
   length0  : UDINT;
END_VAR
VAR_OUTPUT
   ret0 : DINT;
END_VAR;

// -- Sram Speicher als nicht-belegt markieren
FUNCTION GLOBAL SRAM_UnmarkMemory
VAR_INPUT
   reladdr0 :^usint;
   length0  : UDINT;
END_VAR
VAR_OUTPUT
   ret0 : DINT;
END_VAR;

// CONVERT DataSize into alocated size (nrof blocks * CST_SRAM_BLOCKSIZE)

FUNCTION  GLOBAL SRAM_GET_ALLOCATED
 
VAR_INPUT
 DataSize   :UDINT;
END_VAR

VAR_OUTPUT
 AlocSize   :UDINT; 
END_VAR;

TYPE
  _THREADPARAS   : STRUCT
    pRamEx    : ^RamEx;
    hMailbox  : MT_MAILBOX;
    hThread   : MT_TASKHANDLE;
  END_STRUCT;
  
  _THREADDATA    :STRUCT
    objCRC    : UDINT;
    dataLen   : UDINT;
    dataCRC   : UDINT;
    pData     : ^USINT;
    sizeToSet : UDINT;
    atToSet   : UDINT;
  END_STRUCT;
END_TYPE

FUNCTION GLOBAL __cdecl RamExAsyncThread
VAR_INPUT
  pThreadParas : ^_THREADPARAS;
END_VAR;

VAR_PRIVATE
  threadParas   : _THREADPARAS;
END_VAR


//-------------------------
// Calc CKSUM of RAMEX Hdr End MakeIt Valid
//-------------------------

FUNCTION GLOBAL  MakeRamExChk
VAR_INPUT
  pXramCell : ^XramCell;
END_VAR;
 

//[#ENGLISH]
// RAMex stores a variable number of data bytes at power down,
// and reloads it at startup of the PLC/IPC.
//
// RAMex-objects have to be configured as follows:
// -	set the data length in LASAL, this value is used in the
//		constructor of the class to alloc memory
// -	derive Your class from RAMex, if the data buffer should
//		be initialized with default values in the constructor
// or	use a RAMex-object as a component (embedded or stand-alone)
// -	the data from the file active.dat (if one exists on the IPC)
//		 is read afterwards
// -	as soon as the Init()-method is called, the data from the
//		last session is available
//[#DEUTSCH]
// RAMex speichert beim Abschalten der Steuerung oder des IPCs eine
// variable Anzahl von Daten, und lädt diese Daten beim erneuten
// Einschalten.
//
// RAMex-Objekte können folgendermaßen konfiguriert werden:
// -	die Datenlänge in LASAL setzen, der Konstruktor alloziert dann
//		einen entsprechend großen Puffer
// -	eine Klasse von RAMex ableiten, und selber im Konstruktor die
//		_alloc-Funktion aufrufen
// oder ein RAMex-Objekt als Komponente verwenden (eingebettet oder
//		als selbständiges Objekt)
// -	die nullspannungssicheren Daten werden beim Einschalten gelesen
// -	wenn die Init-Methode ihres Objekts aufgerufen wird, können sie
//		bereits auf die Daten des Komponenten-Objekts zugreifen.
FUNCTION  RAMex::RAMex   
VAR_OUTPUT
	ret_code		: CONFSTATES;
END_VAR
VAR
//	udSize			: UDINT;
  pObj           : ^OBJ;
	OldSize			: UDINT;
	NewSize			: UDINT;
	RelAdress		: UDINT;
	CRC				: UDINT;
	pFileAdr	   : ^XFileCell;
	result		   : UDINT;		 	
END_VAR


// wir nutzen die Laenge einer Ramzelle fuer 2 Sachen
// Byte 0,1 wirkliche länge in bytes 
// Byte 2,3 genutzte Länge (z.b 16 byte allocated aber nur 9 byte genutzt)
// damit wir bei restart alle wieder wissen wo der string aus ist.
// wir tragen in ud_allocated = wirkliche länge ein 
// und in m_udlength die genutzte länge

	ret_code	:= C_OK;

  if(IsClientConnected(#UseFile)) then
    ObjectTraceMessage("do not connect client 'UseFile'");
    ret_code := C_SYNTAX_ERROR;
    return;
  end_if;

  UseFile := UseFile.Read();
  
  if(UseFile = 1) then
    RamExToFile();
    return;
  end_if;


// -- kennung das neues Memory System ---	

	IF _S_RAM_Hptr <> 0 then      
	
	  // Falls MerkerEX was alloziert hat, haun wir es weg !!
	  	
	  If m_pData <> 0  & m_udLength<> 0 then
	    OldSize:= m_udLength;
		MerkerEx::Alloc(0);
		m_udLength:=OldSize;
      END_IF;
		

	  //-- Über die CRC32 des Namens suchen ob schon vorhanden 

	  pObj 	:= LSL_GetHdr(this);

		//#SR000 Start
		#ifdef LASAL2_OBJ
			#pragma warning (disable:137)
	  			CRC:=	pObj^.pObjDsc^.SymName.udCRC;
			#pragma warning (default:137)
		#else
		//#SR000 End
	  		CRC:=	pObj^.pObjDsc^.udCRC;
		#endif	//#SR000

	  m_udGrowBy:=SRAM_GET_ALLOCATED(1);      // feststellen in welchen blockgrössen wir arbeiten

      if GetVersion_sRam() < 2 then
	    // suchen oder anlegen eines neuen eintrages
	    pRAMaddr$UDINT:=Find_sRam(CRC)$UDINT;
	    result :=0;	
	  else
	    // suchen oder anlegen eines neuen eintrages
        result := Find_sRamFile(CRC,#pRAMaddr$UDINT,#pFileAdr$XFileCell,1);
	  end_if;	

	  //----------------------------------------------------	
	  // ----  Wir Haben eine Zelle zugewiesen bekommen --
	  //----------------------------------------------------	

	  If  ((pRAMaddr <> 0) & (result =0)) Then

	  	// -- wenn Daten im Ram ungültig, initialisieren

	    If (pRAMaddr^.udType and 16#8000_0000) = 0 then

          MELKUS_ENTRY:
		  // 	
          // Länge über loader Vorinitialisieren 
		  //	
		  IF m_udLength THEN   

			 RelAdress$UDINT:= (SRAM_GetMemory(m_udLength))$UDINT;

			 // --- Speicher ist zugewiesen worden --
			 If RelAdress <> 0 THEN
			   GET_USED_S_RAM();
			   m_udAllocated		  := SRAM_GET_ALLOCATED(m_udLength);
		  	   pRAMaddr^.udData$UDINT := (m_udLength shl 16) or (m_udAllocated and 16#ffff); 
			   pRAMaddr^.udType 	  := RelAdress;
			   m_pData				  := _S_RAM_Hptr^.DataStart+RelAdress;
			   _memset(m_pData, 0,m_udLength);   // Speicher mi 0 vorladen
	           MakeRamExChk(pRAMaddr);	
			
			 // -- Kein speicher verfügbar !!
			 ELSE  
			   m_udLength		:=0;
			   pRAMaddr^.udType :=0;
			   m_pData			:=NIL;
			   m_udAllocated	:=0;
			   ret_code			:= C_OUTOF_NEAR;		// memory allocation failed

			 end_if;
		
		  //-- Init auf LNG = 0
		  // --> falls schon speicher belegt war , freigeben			
		  ELSE
		  	Alloc(0);
		  END_IF;

		//-------------------------
	  	// -- Daten im Ram gültig
		//-------------------------

		ELSE
		  //- Header war da, aber noch kein Speicher aloziert --> schaun ob init	
		  If pRAMaddr^.udData$dint = 0 then
            GOTO MELKUS_ENTRY;

		  // - Gültige daten im speicher. jetzt wird es komplizierter
		  ELSE
   			m_pData:=_S_RAM_Hptr^.DataStart+(pRAMaddr^.udType and 16#00FF_ffff);
			OldSize:=pRAMaddr^.udData and 16#ffff;
			m_udAllocated:=SRAM_GET_ALLOCATED(OldSize);
		  	
		    		
		  	//   wenn im konstructor längenwunsch: zurechtallozieren
			IF m_Udlength  > OldSize then
				NewSize   := m_Udlength;
				m_Udlength:= OldSize;

	            IF alloc(NewSize) = 0 then 
			      ret_code	:= C_OUTOF_NEAR;		// memory allocation failed
				else
 			      m_udAllocated:=SRAM_GET_ALLOCATED(NewSize);
			      m_udLength:=pRAMaddr^.udData shr 16;
				end_if;

			// Konstructor will keine Länge setzen, alte nehmen
			ELSE
			  m_udLength:=pRAMaddr^.udData shr 16;
			  MakeRamExChk(pRAMaddr);
			END_IF;
          END_IF; 
		END_IF; 
	
      ELSE
	  //-----------------------------------------------------------	
	  // DAS WIRD NIX ! WIR bekommen keine zelle mehr zugewiesen !!!
	  // --> kein Speicher mehr vorhanden im S-RAM	
	    m_pData		:= NIL;
		m_udLength	:= 0;
		ret_code	:= C_OUTOF_NEAR;		// memory allocation failed
	  END_IF;
	  
    ELSE
      // Kein Sram vorhanden
      TRACE_ERR("no SRAM available on this platform (needed by a RAMex object)");
      ret_code	:=  C_INCOMPATIBLE;
	end_if;

  
  // Gibt es ein Daten File, wenn ja die Daten übernehmen
  if(ret_code = C_OK) then
    LoadFile(TRUE);
  end_if;
  
END_FUNCTION // RAMex::RAMex
//[#ENGLISH]
// The function is called by the operating system on power-down.
// It returns the (4 byte) length value and writes the extra
// data to the record set opened by the OS (on an IPC only).
//[#DEUTSCH]
// Diese Funktion wird vom Betriebssystem beim Abschalten aufgerufen.
// Sie liefert die Pufferlänge und schreibt den internen Datenpuffer
// in die geöffnete active.dat (am IPC).
 //VIRTUAL GLOBAL RAMex::m_udLength::Read
//[#ENGLISH]
// The function is called by the operating system on startup.
// It gets the (4 byte) length value and reads the extra data.
// (on IPCs only)
//[#DEUTSCH]
// Diese Funktion wird vom Betriebssystem beim Einschalten aufgerufen.
// Sie bekommt eine Längenangabe übergeben, und liest eine entsprechende
// Anzahl Bytes aus der active.dat. (nur am IPC)

 //VIRTUAL GLOBAL RAMex::m_udLength::Write

//[#DEUTSCH]
//Diese Funktion kann die Größe des Datenpuffers ändern.
//[>udSize]		neue Größe, 0 gibt den Puffer frei
//[#ENGLISH]
// The function is called to resize the data buffer of an object.
//[>udSize] 	new size, 0 frees the buffer
//[<pCode]pointer to allocated block of memory 0= ko memory available

FUNCTION VIRTUAL GLOBAL RAMex::Alloc
VAR_INPUT
	udSize		: UDINT;
END_VAR
VAR_OUTPUT
	pCode		: ^void;
END_VAR
VAR
	RelAdress		: UDINT;
	OldAdress		: UDINT;
    OldAllocated : UDINT;
END_VAR

  if(UseFile = 1) then
    pCode := AllocToFile(udSize);
    return;
  end_if;
  
	if udSize > 16#ffff then
	   // a miximum size of 64 kBYte is allowed thats it 
	   TRACE ("RAMex block maxmimum 64 kByte");	
	   GOTO MEMORY_IS_GONE;
	END_IF;
    
	// Wenn schon daten alloziert waren gehts los
	IF m_pData THEN
	  IF udSize THEN
	    // kürzer machen ist einfach !
		m_udAllocated := SRAM_GET_ALLOCATED(pRAMaddr^.udData and 16#ffff);

	    If udSize <= m_udLength THEN
		  m_udLength:=udSize;	
		  pRAMaddr^.udData := (pRAMaddr^.udData and 16#ffff) or udSize shl 16; 	

        // Block verlängern
		ELSE
		  // passt noch in den allozierten bereich !	
		  IF udSize <= m_udAllocated THEN
			_MEMSET((m_pdata+m_udLength),0,(udSize-m_UdLength)); // hinten löschen
		    m_udLength := udSize;	
		    pRAMaddr^.udData := (pRAMaddr^.udData and 16#ffff) or udSize shl 16; 	
		  
		  // Passt nicht mehr, neuen machen umkopieren und so weiter	
		  ELSE
		    OldAdress$^VOID := m_pData$^VOID;
            OldAllocated := m_udAllocated;
            
            // Wenn SRAM_MarkMemory mit einer Länge mit gesetzem obersten Bit aufgerufen wird, 
            // dann wird der Bereich allokiert, wenn zuvor alles frei war. Das wird verwendet 
            // für einen Versuch um den zusätzlichen Speicher direkt hinter dem aktuellen Block 
            // zu allokieren.
            IF SRAM_MarkMemory(OldAdress$^USINT + OldAllocated - _S_RAM_Hptr^.DataStart$UDINT, 
                               (udSize - OldAllocated) OR 16#80000000) = 0 THEN
                // der zusätzliche Speicher konnte direkt hinter dem Block allokiert werden -> kein umkopieren notwendig
                  m_udAllocated := SRAM_GET_ALLOCATED(udSize);
                  pRAMaddr^.udData := (m_udAllocated and 16#ffff) or udSize shl 16; 	
                  m_udLength := udSize;	
            ELSE
                // der zusätzliche Speicher konnte nicht hinter dem Block allokiert werden -> umkopieren
                RelAdress$UDINT := (SRAM_GetMemory(udSize))$UDINT;
                If RelAdress <> 0 THEN
                  m_udAllocated := SRAM_GET_ALLOCATED(udSize);
                  pRAMaddr^.udData := (m_udAllocated and 16#ffff) or udSize shl 16; 	
                
    //***			  pRAMaddr^.udData$UDINT :=udSize; 
                  pRAMaddr^.udType := RelAdress;
                  m_pData := _S_RAM_Hptr^.DataStart + RelAdress;
                  _memcpy(m_pdata$^VOID, OldAdress$^VOID, m_udLength);
                  _MEMSET((m_pdata+m_udLength), 0, (udSize-m_UdLength)); // hinten löschen
                  m_udLength := udSize;	
                
                  SRAM_UnmarkMemory(OldAdress$^USINT - _S_RAM_Hptr^.DataStart$UDINT, OldAllocated);
                  GET_USED_S_RAM();

                 // wir bekommen keinen speicher !! pech gehabt
                 ELSE
                   GOTO MEMORY_IS_GONE;
                 END_IF;
            END_IF;
		  END_IF; 
		END_IF;

	  //------------	
	  // ALLOC(0) .. Speicher freigeben

	  ELSE
		// FREE_S_RAM (StartOffset:= .. , LNG := )   // Fehlt Noch !!
		GOTO MEMORY_IS_GONE;
	  END_IF;
	  	

	// es sind keine Daten alloziert (das ist der einfachere fall
    ELSE
	  // Neu allozieren
	  	
	  IF udSize THEN
	  	m_udLength:=UdSize;
		RelAdress$UDINT:= (SRAM_GetMemory(m_udLength))$UDINT;
		If RelAdress <> 0 THEN
		   GET_USED_S_RAM();
		   m_udAllocated:=SRAM_GET_ALLOCATED(m_udLength);  
		   
		   pRAMaddr^.udData := (m_udAllocated and 16#ffff) or udSize shl 16; 	
		   pRAMaddr^.udType :=RelAdress;
		   m_pData:=_S_RAM_Hptr^.DataStart+RelAdress;
		   _memset(m_pData, 0,m_udLength);

 		 // wir bekommen keinen speicher !! pech gehabt

		 ELSE
		   GOTO MEMORY_IS_GONE;
		 END_IF;

	  // nicht vorhandenen speicher löschen ist einfach 
	  			
	  ELSE
MEMORY_IS_GONE:
		 m_udAllocated := m_udLength := m_pdata$UDINT := 0;
		 pRAMaddr^.udData$UDINT :=m_udLength; 
		 pRAMaddr^.udType :=0;
	  END_IF;	
	END_IF;

	MakeRamExChk(pRAMaddr);	
	pCode$UDINT:=m_pData$UDINT;


END_FUNCTION //VIRTUAL GLOBAL RAMex::Alloc

// Handles a message, that is sent by the loader, when the
// application is running on a x386, and if there is retentive
// data, that belonged to the same object during the last run
// of the application.
// The object frees its currently allocated data and uses the
// retentive data instead.
FUNCTION VIRTUAL GLOBAL RAMex::m_udLength::NewInst
VAR_INPUT
	pPara		: ^CmdStruct;
	pResult		: ^Results;
END_VAR
VAR_OUTPUT
	ret_code		: IprStates;
END_VAR
VAR
	pMem			: ^void;
	pEx		        : ^RamEx;
	tmp				: RamEx;
	udCRC32_1		: UDINT;
	udCRC32_2		: UDINT;
END_VAR

  if(UseFile = 1) & (pPara^.uiCmd <> SAVE_DATA_TO_RAMEXFILE$UINT) then
    ret_code := MerkerEx::NewInst(pPara, pResult);
    return;
  end_if;
  
	ret_code	:= READY;

	CASE pPara^.uiCmd OF

		// assign a pointer to retentive data to the object
	LSL_MSG_RETENTIVE:
		IF this <> pPara^.aPara[2]$^RamEx THEN
			ret_code := ERROR;
			RETURN;
		END_IF;

		// if this block of memory was already allocated
		// by the current object, then leave it as it is!
    //flags := Lock_On();
		pMem	:= pPara^.aPara[0]$^void;

		IF pMem = m_pData THEN
			// the new data has got this length
			m_udLength	:= pPara^.aPara[1]$UDINT;
    ELSE
      IF m_pData THEN
	  	  SetSize(0);		// free old data
		  END_IF;

		  m_pData		:= pMem$^usint;
		  m_udLength	:= pPara^.aPara[1]$UDINT;
		END_IF;
    //Lock_Off(flags);
	// Daten von 2 ramexobjekten vertauschen (füer IPR genützt !!)
    // War falscher weise In der RAMEX

	EXCHANGE_DATA_PTR $ UDINT:

    LockOn();
   
		// der kompiler erkennt nicht das wir nicht auf eine fremde klasse sondern nur
		// auf ein fremdes objekt zugreifen !!!
		#pragma warning (disable : 137)

		pEX	:= pPara^.aPara[0]$^RamEx;

		// Unsere daten ins Temporare
			
		Tmp.m_udLength			:=This^.m_udLength;		
		Tmp.m_pData				:=This^.m_pData;
		Tmp.m_udAllocated		:=This^.m_udAllocated;
		Tmp.pRAMaddr			:=This^.pRAMaddr;

		// das sind die Namen		
		udCRC32_1				:=this^.pRAMaddr^.udCRC32;
		udCRC32_2				:=pEx^.pRAMaddr^.udCRC32;

		// unsere vom fremden laden
		This^.m_udLength		:=pEX^.m_udLength;		
		This^.m_pData			:=pEX^.m_pData;
		This^.m_udAllocated		:=pEX^.m_udAllocated;
		This^.pRAMaddr			:=pEX^.pRAMaddr;

		// Fremdes Aus dem Zwischenspeicher (das ist unser altes) laden
		pEX^.m_udLength		    :=Tmp.m_udLength;		
		pEX^.m_pData			:=Tmp.m_pData;
		pEX^.m_udAllocated		:=Tmp.m_udAllocated;
		pEX^.pRAMaddr			:=Tmp.pRAMaddr;

		// jetzt schreiben wir auf die bereits ausgetauschten pointer
		// die namen um (die udCRC32 = der Name des Ramex)
		this^.pRAMaddr^.udCRC32 := udCRC32_1;
		pEx^.pRAMaddr^.udCRC32  := udCRC32_2;

		// neue chk berechnen weil der name auch in der chk ist !!!
		pRAMaddr^.udChk  := pRAMaddr^.udData+pRAMaddr^.udCRC32+(pRAMaddr^.udType and 16#00FFFFFF);
		pEx^.pRAMaddr^.udChk  := pEx^.pRAMaddr^.udData+pEx^.pRAMaddr^.udCRC32+(pEx^.pRAMaddr^.udType and 16#00FFFFFF);

    LockOff();
  
		#pragma warning (default : 137)

    SAVE_DATA_TO_RAMEXFILE $ UDINT:
    if(UseFile = 1) then
      UpdateToFile();
    end_if;
			
	ELSE
		ret_code	:= MerkerEx::NewInst(pPara, pResult);
	END_CASE;

END_FUNCTION //VIRTUAL GLOBAL RAMex::m_udLength::NewInst

FUNCTION VIRTUAL GLOBAL RAMex::m_udLength::Write
	VAR_INPUT
		input (EAX) 	: UDINT;
	END_VAR
	VAR_OUTPUT
		result (EAX) 	: UDINT;
	END_VAR

	m_udLength := input;
 	result     := input;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL RAMex::m_udLength::Read
	VAR_OUTPUT
		output (EAX) 	: UDINT;
	END_VAR

	output := m_udLength;

END_FUNCTION

//[#ENGLISH]
//Use this function to change the size of the data buffer
//[>udSize] 	new size, 0 frees the buffer
//[<ret_code]	C_OK if memory could be allocated, C_OUTPF_NEAR	if a failure occured
//[#DEUTSCH]
//Diese Funktion kann die Größe des Datenpuffers ändern.
//[>udSize]		neue Größe, 0 gibt den Puffer frei
//[<ret_code]	C_OK bei Erfolg, C_OUTPF_NEAR bei zuwenig Speicher

FUNCTION VIRTUAL GLOBAL RAMex::SetSize
VAR_INPUT
	udSize		: UDINT;
END_VAR
VAR_OUTPUT
	ret_code		: ConfStates;
END_VAR
VAR
	pData			: ^USINT;
	udOldLen		: UDINT;
END_VAR

  if(UseFile = 1) then
    ret_code := SetSizeToFile(udSize);
    return;
  end_if;


	ret_code	:= C_OK;

	IF m_pData = NIL THEN
		m_udLength := 0;
	END_IF;

	IF udSize <> 0 & GetNewBlockSize(udSize) THEN	// ok if length didn't change	//#SR001 (insert udSize <> 0)
	
		udOldLen	:= m_udLength;

		pData		:= Alloc(m_udAllocated)$^USINT;

		IF udSize & pData = NIL THEN
			ret_code := C_OUTOF_NEAR;
			RETURN;
		END_IF;

		m_pData	:= pData;
		m_udLength := udSize;

		// special we have to keep in memory the used length
		pRAMaddr^.udData := (m_udLength shl 16) or (m_udAllocated and 16#ffff); 
		pRAMaddr^.udChk  := pRAMaddr^.udData+pRAMaddr^.udCRC32+(pRAMaddr^.udType and 16#00FFffff);
		ZeroMemory(udOldLen);			// initialize the new part
    ELSE
		m_udLength := udSize;
		// special we have to keep in memory the used length
		pRAMaddr^.udData := (m_udLength shl 16) or (m_udAllocated and 16#ffff); 
		pRAMaddr^.udChk  := pRAMaddr^.udData+pRAMaddr^.udCRC32+(pRAMaddr^.udType and 16#00FFffff);
	END_IF;


END_FUNCTION //VIRTUAL GLOBAL RAMex::SetSize

//[#ENGLISH]
//This function is only necessary, if the RaxEx is using file storage.
//It has to be called after manipulating data, if the data has been changed by using a
//pointer to the data (GetDataPtr).
//[#DEUTSCH]
//Diese Funktion ist nur notwendig, wenn die RamEx Daten in einem File gespeichert werden.
//Sie muss nach Manipulatin der Daten aufgerufen werden, wenn diese per Datenpointer
//(GetDataPtr) geändert wurden.
FUNCTION VIRTUAL GLOBAL RAMex::UpdateToFile

  if(UseFile = 1) then
    SetDataAtToFile(m_udLength, 0);
  end_if;
  
END_FUNCTION

FUNCTION VIRTUAL GLOBAL RAMex::SetDataAt
VAR_INPUT
  pData 	: ^USINT;
  udSize 	: UDINT;
  udAt 	: UDINT;
END_VAR
VAR_OUTPUT
  ret_code 	: ConfStates;
END_VAR

  LockOn();
  
  ret_code := MerkerEx::SetDataAt(pData, udSize, udAt);
  
  if(UseFile = 1) then
    ret_code := C_OUTOF_NEAR;
    if(SetDataAtToFile(udSize, udAt) = TRUE) then
      ret_code := C_OK;
    end_if;
  end_if;

  LockOff();
    
END_FUNCTION

FUNCTION VIRTUAL GLOBAL RAMex::SetData
VAR_INPUT
  pData 	: ^USINT;
END_VAR

  LockOn();

  MerkerEx::SetData(pData);
  
  if(UseFile = 1) then
    SetDataAtToFile(m_udLength, 0);
  end_if;

  LockOff();

END_FUNCTION

FUNCTION RAMex::CreateFileName
VAR_INPUT
	pFileName 	: ^USINT;
END_VAR
VAR_OUTPUT
  crc         : UDINT;
END_VAR
VAR
  pObj        : ^Obj;
  len         : UDINT;
END_VAR

  crc := 0;
  
  pFileName^ := 0;

  pObj := LSL_GetHdr(this);
  
  if(pObj <> NIL) then

    #ifdef LASAL2_OBJ
      #pragma warning (disable:137)
      crc := pObj^.pObjDsc^.SymName.udCRC;
      #pragma warning (default:137)
    #else
      crc := pObj^.pObjDsc^.udCRC;
    #endif

    _strcpy(pFileName, RAMEXFILES);
    _strcat(pFileName, "\");
    len := _strlen(pFileName);
    _memset(pFileName + len, 0, 9);
    _itoa(crc, pFileName + len);
  end_if;

END_FUNCTION

FUNCTION RamEx::GetObjectCRC
VAR_OUTPUT
  crc   : UDINT;
END_VAR
VAR
  pObj        : ^Obj;
END_VAR

  crc := 0;
  
  pObj := LSL_GetHdr(this);
  
  if(pObj <> NIL) then

    #ifdef LASAL2_OBJ
      #pragma warning (disable:137)
      crc := pObj^.pObjDsc^.SymName.udCRC;
      #pragma warning (default:137)
    #else
      crc := pObj^.pObjDsc^.udCRC;
    #endif
  end_if;

END_FUNCTION

FUNCTION RAMex::RamExToFile
VAR_OUTPUT
	ret_code 	: ConfStates;
END_VAR
VAR
	htMB    : MT_MAILBOX;
  fHandle : DINT;
  crc     : UDINT;
END_VAR

  htMB := MultiTask.CREATEMAILBOX(sizeof(_THREADDATA), RAMEXMAILBOXSIZE, "RamExAsyncMailbox");
  if(htMB <> NIL) then
    // Erstes RamEx Objekt
    threadParas.pRamEx := this;
    threadParas.hMailbox :=  htMB;

    // Priorität Background + 1
    threadParas.hThread := MultiTask.CREATETHREAD(#RamExAsyncThread(), 8, 16#1000, 0, #threadParas, "RamExAsyncThread");
    
    OS_FILE_DELETE(RAMEXINDEXFILE);
    OS_FILE_CREATEDIR(RAMEXFILES);
  end_if;

  fHandle := OS_FILE_OPEN(RAMEXINDEXFILE, 2, ATT_READ_WRITE or ATT_CREATE);
  if(fHandle > 0) then
    OS_FILE_SEEK(fHandle, 0, FILE_END);
    crc := GetObjectCRC();
    if(OS_FILE_WRITE(fHandle, #crc, sizeof(crc)) <> sizeof(crc)) then
      TRACE("RamEx - Can not write INDEX file");
    end_if;
  end_if;
  
  if(fHandle <= 0) then
    TRACE("RamEx - Can not write INDEX file");
  else
    OS_FILE_CLOSE(fHandle);
  end_if;
  
  if(LoadFile(FALSE) = TRUE) then
    ret_code := C_OK;
  else
    if(CreateFile(TRUE) = TRUE) then
      ret_code := C_OK;
    end_if;
  end_if;
  
END_FUNCTION

//FUNCTION RAMEx::LoadFileToSRAM

FUNCTION RAMex::AllocToFile
VAR_INPUT
  udSize 	: UDINT;
END_VAR
VAR_OUTPUT
  pCode 	: ^void;
END_VAR

  pCode := MerkerEx::Alloc(udSize);

END_FUNCTION

FUNCTION RAMex::CreateFile
VAR_INPUT
  saveFromSRAM  : BOOL;
END_VAR
VAR_OUTPUT
	ret_code 	: BOOL;
END_VAR
VAR
  fileName    : ARRAY [0..255] OF CHAR;
  objectCRC   : UDINT;
	fHandle     : DINT;
  pData    	  : ^USINT;
  crc         : UDINT;
  pRamCell    : ^XRamCell;
  ramAddr     : UDINT;
  fileCell    : XFileCell;
  sramDescr   : ^MRAM_DESCR;
END_VAR

  ret_code := FALSE;

  objectCRC := CreateFileName(#fileName[0]);

  pData := NIL;
  pRamCell := NIL;
  ramAddr := 0;

  if(saveFromSRAM = TRUE) then 

    if(objectCRC <> 0) then
    
      if(GetVersion_sRam() < 2) then
        pRamCell := Find_sRamEx(objectCRC, TRUE);
        if(pRamCell <> NIL) then
          // Hier wird immer vom SRAM kopiert, da beim REORG die Daten von der Kopie
          // ins SRAM kopiert werden.
          pData := _s_ram_hptr^.DataStart + (pRamCell^.udType and 16#00FFFFFF);
        end_if;

      else
        if((ops.uiLoaderVersion.usHiRev >= 16#22) & (ops.uiLoaderVersion.usLoRev >= 139)) then
          ramAddr := 1;
          // ramAddr wird auf 1 initialisiert um den Loader kompatibel zu halten
          // 1 bewirkt, dass die Funktion "Find_sRamFileEx" beim Rückgabewert nicht 0 für gefunden liefert,
          // sondern entweder _s_Ram_hptr oder _s_Ram_hptr_Kopie (REORG) um auf die SRAM Daten zugreifen zu können.

          sramDescr := Find_sRamFileEx(objectCRC, #ramAddr, #fileCell, 1, TRUE)$^MRAM_DESCR;
          if((sramDescr <> NIL) & (ramAddr <> 0) & (ramAddr <> 1)) then
            pRamCell := ramAddr$^XRamCell;
            pData := sramDescr^.DataStart + (pRamCell^.udType and 16#00FFFFFF);
          end_if;

        else
          // Alter Loader - das ist zwar falsch aber so funktioniert es wenigstens wenn,
          // die SRAM Daten reorgansiert sind.
          
          if(Find_sRamFileEx(objectCRC, #ramAddr, #fileCell, 1, TRUE) = 0) then
            if(ramAddr <> 0) then
              pData := (ramAddr + sizeof(XRamCell))$^USINT;
              pRamCell := ramAddr$^XRamCell;
            end_if;
          end_if;
        end_if;
      end_if;

      if((pData <> NIL) & (pRamCell <> NIL)) then
        if(MerkerEx::SetSize(pRamCell^.udData shr 16) = C_OK) then
          MerkerEx::SetData(pData);
          pData := MerkerEx::GetDataPtr();
          LDR_ForceSramReorgOnNextRun();
        end_if;        
      end_if;
    end_if;
  end_if;

  if((saveFromSRAM = FALSE) | (pData = NIL)) then 
    pData := MerkerEx::GetDataPtr();
  end_if;

  if(filename[0] <> 0) then

    fHandle := OS_FILE_OPEN(#filename[0], 2, ATT_CREATE);
    if(fHandle > 0) then

      if(OS_FILE_WRITE(fHandle, #m_udAllocated, sizeof(m_udAllocated)) = sizeof(m_udAllocated)) then

        crc := 16#FFFFFFFF;
        if(pData <> NIL) then
          crc := CRC32_Buffer(pData, m_udAllocated);
        end_if;
        
        if(OS_FILE_WRITE(fHandle, #crc, sizeof(crc)) = sizeof(crc)) then
          ret_code := TRUE;

          if(pData <> NIL) then
            if(OS_FILE_WRITE(fHandle, pData, m_udAllocated) <> m_udAllocated) then
              ret_code := FALSE;
            end_if;
          end_if;
            
        end_if;
      end_if;
      
      OS_FILE_CLOSE(fHandle);
    end_if;
  end_if;

END_FUNCTION

FUNCTION RAMex::LoadFile
VAR_INPUT
  loadToSRAM 	: BOOL;
END_VAR
VAR_OUTPUT
  ret_code 	: BOOL;
END_VAR
VAR
  fileName  : ARRAY [0..255] OF CHAR;
	fHandle   : DINT;
  fileLen   : UDINT;
  crc       : UDINT;
  pData     : ^USINT;
END_VAR

  ret_code := FALSE;

  CreateFileName(#fileName[0]);
  if(filename[0] <> 0) then
  
    fHandle := OS_FILE_OPEN(#filename[0], 2, ATT_READ_ONLY);
    if(fHandle > 0) then

      if(OS_FILE_READ(fHandle, #fileLen, sizeof(fileLen)) <> sizeof(fileLen)) then
        goto LoadFile_ERROR;
      end_if;

      // Ist die Länge OK oder kann die Länge gesetzt werden
      if(fileLen > m_udAllocated) then
        // SRAM oder RamEx
        if(loadToSRAM = FALSE) then
          if(MerkerEx::SetSize(fileLen) <> C_OK) then
            goto LoadFile_ERROR;
          end_if;
        else
          if(SetSize(fileLen) <> C_OK) then
            goto LoadFile_ERROR;
          end_if;
        end_if;
      end_if;

      if(fileLen > 0) then
        // Das ist der normale Ablauf, im else wird Länge 0 behandelt

        // Jetzt noch den Datenpointer setzen, der könnte sich durch SetSize ändern, also nachher
        // SRAM oder MerkerEx
        if(loadToSRAM = FALSE) then
          pData := MerkerEx::GetDataPtr();
        else
          // Die Methode gibt es zwar nicht, aber falls mal jemand aud die Idee kommt, diese zu überschreiben
          pData := GetDataPtr();
        end_if;

        if(OS_FILE_READ(fHandle, #crc, sizeof(crc)) = sizeof(crc)) then

          // Jetzt können die Daten gelesen werden (falls der MerkerEx Pointer gültig ist)
          if(pData <> NIL) then
            if(OS_FILE_READ(fHandle, pData, fileLen) = fileLen) then

              // Ist die CRC gültig ??
              if(crc = CRC32_Buffer(pData, fileLen)) then

                // Wenn die Daten ins SRAM kopiert wurden, kann das File gelöscht werden
                if(loadToSRAM = TRUE) then
                  OS_FILE_CLOSE(fHandle);
                  OS_FILE_DELETE(#fileName[0]);
                end_if;
        
                ret_code := TRUE;
              end_if;
            end_if;
          end_if;
        end_if;
      else
        // Unguter Fall, File mit keinen Daten
        if(loadToSRAM = TRUE) then
          OS_FILE_CLOSE(fHandle);
          OS_FILE_DELETE(#fileName[0]);
          
          ret_code := TRUE;
        end_if;
      end_if;
      
    end_if;
  end_if;


LoadFile_ERROR:
  if(fHandle > 0) then
    OS_FILE_CLOSE(fHandle);
  end_if;

END_FUNCTION

FUNCTION Get_ITASK_Version
VAR_OUTPUT
	version : UDINT;
END_VAR
  
  // Anm.: Beim Salamander darf das udVersion Element aus der LSL_ITASK Struktur 
  // (Makro OS_ITASK_VERSION) nicht interpretiert werden, weil hier keine Versionsnummer 
  // dahintersteckt, sondern ein Funktionszeiger auf eine Dummy-Funktion.
  IF _RtOSVersion < 16#9000 THEN
    version := OS_ITASK_VERSION;
  ELSE
    version := 0;
  END_IF;
  
END_FUNCTION

(*
  Liefert die Info, ob das OS versucht, die Applikation zu stoppen
*)
FUNCTION ITASK_IS_STOPPING
VAR_OUTPUT
  is_stopping : UDINT;
END_VAR

  IF Get_ITASK_Version() >= 0x11 THEN
    is_stopping := OS_ITASK_IS_STOPPING();
  ELSE
    is_stopping := 0;
  END_IF;

END_FUNCTION

(*
  Inkrementiert oder dekrementiert den StopWait Zähler. 
  Wenn dieser >0 ist dann wartet das OS beim Stoppen der Applikation, bis dieser 
  Zähler auf 0 geht. Die Applikation kann damit das Stoppen der Applikation 
  verzögern bis z,B. alle Retentive File Server geschrieben sind.
*)
FUNCTION ITASK_SET_STOPWAIT_CNTR
VAR_INPUT
  inc_dec : DINT;
END_VAR

  IF Get_ITASK_Version() >= 0x11 THEN
    OS_ITASK_SET_STOPWAIT_CNTR(inc_dec);
  END_IF;

END_FUNCTION

FUNCTION RAMex::SetDataAtToFile
	VAR_INPUT
		udSize 	: UDINT;
		udAt 	: UDINT;
	END_VAR
	VAR_OUTPUT
		ret_code 	: BOOL;
	END_VAR
VAR
  pData     : ^USINT;
  threadData : _THREADDATA;
END_VAR

  ret_code := FALSE;

  pData := MerkerEx::GetDataPtr();
  if(pData <> NIL & !!ITASK_IS_STOPPING()) then  // keine Schreibrequests mehr annehmen, wenn das OS versucht, die Applikation zu beenden
    ret_code := TRUE;
    threadData.objCRC := GetObjectCRC();
    threadData.dataCRC := CRC32_Buffer(pData, m_udLength);
    threadData.dataLen := m_udLength;
    threadData.pData := pData;
    threadData.sizeToSet := udSize;
    threadData.atToSet := udAt;
    
    if(MultiTask.PUTCOND(threadParas.hMailbox, #threadData) = FALSE) then
      TRACE("RamEx - Can not PUT to mailbox - Please define RAMEXMAILBOXSIZE heigher than 2000");
      ret_code := FALSE;
    else
      ITASK_SET_STOPWAIT_CNTR(1);
    end_if;
  end_if;

END_FUNCTION

FUNCTION RAMex::SetSizeToFile
VAR_INPUT
  udSize 	: UDINT;
END_VAR
VAR_OUTPUT
  ret_code 	: ConfStates;
END_VAR
VAR
  pData     : ^USINT;
  threadData : _THREADDATA;
END_VAR

  ret_code := MerkerEx::SetSize(udSize);
  if(ret_code <> C_OK) then
    return;
  end_if;

  if !!ITASK_IS_STOPPING() then // keine Schreibrequests mehr annehmen, wenn das OS versucht, die Applikation zu beenden
  pData := MerkerEx::GetDataPtr();

  threadData.objCRC := GetObjectCRC();
  threadData.dataCRC := CRC32_Buffer(pData, m_udLength);
  threadData.dataLen := m_udLength;
  threadData.pData := pData;
  threadData.sizeToSet := 16#FFFFFFFF;
  threadData.atToSet := 16#FFFFFFFF;
  
  if(MultiTask.PUTCOND(threadParas.hMailbox, #threadData) = FALSE) then
      TRACE("RamEx - Can not PUT to mailbox - Please define RAMEXMAILBOXSIZE heigher than 2000");
      ret_code := C_OUTOF_NEAR;
    else
        ITASK_SET_STOPWAIT_CNTR(1);
    end_if;
  end_if;

END_FUNCTION

FUNCTION CreateFileNameThread
VAR_INPUT
  pFileName   : ^CHAR;
  crc         : UDINT;
END_VAR
VAR
  len         : UDINT;
END_VAR

  pFileName^ := 0;
  
  _strcpy(pFileName, RAMEXFILES);
  _strcat(pFileName, "\");
  len := _strlen(pFileName);
  _memset(pFileName + len, 0, 9);
  _itoa(crc, pFileName + len);

END_FUNCTION

FUNCTION RamExAsyncThreadWrite
VAR_INPUT
  fHandle       : DINT;
  pThreadData   : ^_THREADDATA;
END_VAR
VAR_OUTPUT
  ok            : BOOL;
END_VAR
VAR
  fileLen       : UDINT;
  writeAll      : BOOL;
END_VAR

  ok := FALSE;
  writeAll := FALSE;

  if((OS_FILE_READ(fHandle, #fileLen, sizeof(fileLen)) <> sizeof(fileLen)) | 
     ((pThreadData^.atToSet + pThreadData^.sizeToSet) > fileLen)) then    // file zu klein

    // Filedaten können nicht gelesen werden oder File zu klein - alles neu schreiben
    pThreadData^.sizeToSet := pThreadData^.dataLen;
    pThreadData^.atToSet := 0;
    writeAll := TRUE;
  end_if;
  
  if(OS_FILE_SEEK(fHandle, 0, FILE_BEGIN) = 0) then

    if(OS_FILE_WRITE(fHandle, #pThreadData^.dataLen, sizeof(pThreadData^.dataLen)) = sizeof(pThreadData^.dataLen)) then

      if(OS_FILE_WRITE(fHandle, #pThreadData^.dataCRC, sizeof(pThreadData^.dataCRC)) = sizeof(pThreadData^.dataCRC)) then
    
        if(writeAll = FALSE) then
          if(OS_FILE_SEEK(fHandle, FILEHEADERSIZE + pThreadData^.atToSet, FILE_BEGIN) <> FILEHEADERSIZE + pThreadData^.atToSet) then

          end_if;
        end_if;

        ok := TRUE;
        if(pThreadData^.pData <> NIL) then
          if(OS_FILE_WRITE(fHandle, pThreadData^.pData + pThreadData^.atToSet, pThreadData^.sizeToSet) <> pThreadData^.sizeToSet) then
            oK := FALSE;
          end_if;
        end_if;
      end_if;
    end_if;
  end_if;

END_FUNCTION

FUNCTION RamExAsyncThreadSetSize
VAR_INPUT
  fHandle       : DINT;
  pThreadData   : ^_THREADDATA;
END_VAR
VAR_OUTPUT
  ok            : BOOL;
END_VAR

  ok := FALSE;
  
  if(OS_FILE_WRITE(fHandle, #pThreadData^.dataLen, sizeof(pThreadData^.dataLen)) = sizeof(pThreadData^.dataLen)) then

    if(OS_FILE_WRITE(fHandle, #pThreadData^.dataCRC, sizeof(pThreadData^.dataCRC)) = sizeof(pThreadData^.dataCRC)) then

      ok := TRUE;
      if(pThreadData^.pData <> NIL) then
        if(OS_FILE_WRITE(fHandle, pThreadData^.pData, pThreadData^.dataLen) <> pThreadData^.dataLen) then
          ok := FALSE;
        end_if;
      end_if;
      
      OS_FILE_TRUNCATE(fHandle);
    end_if;
  end_if;

END_FUNCTION

FUNCTION GLOBAL __cdecl RamExAsyncThread
VAR_INPUT
  pThreadParas : ^_THREADPARAS;
END_VAR
VAR
  pRamEx      : ^RAMex;
  hMailbox    : MT_MAILBOX;
  threadData  : _THREADDATA;

  fileName  : ARRAY [0..255] OF CHAR;
	fHandle   : DINT;
  writeAll  : BOOL;
  oK        : BOOL;
  reduce    : BOOL;
END_VAR

  if((pThreadParas = NIL) | (pThreadParas^.pRamEx = NIL) | (pThreadParas^.hMailbox = NIL)) then
    TRACE("RamEx Thread - invalid parameters");
    return;
  end_if;
  
	pRamEx := pThreadParas^.pRamEx;
  hMailbox := pThreadParas^.hMailbox;


  while(1) do
    pRamEx^.MultiTask.Get(hMailbox, #threadData);

    writeAll := FALSE;
    oK := FALSE;
    reduce := FALSE;
    
    CreateFileNameThread(#fileName[0], threadData.objCRC);
    if(filename[0] <> 0) then

      fHandle := OS_FILE_OPEN(#filename[0], 2, ATT_READ_WRITE or ATT_CREATE);
      if(fHandle > 0) then

        if((threadData.atToSet <> 16#FFFFFFFF) & (threadData.sizeToSet <> 16#FFFFFFFF)) then
          // Schreibbefehl
          ok := RamExAsyncThreadWrite(fHandle, #threadData);
        else
          // SetSize
          ok := RamExAsyncThreadSetSize(fHandle, #threadData);
        end_if;

        OS_FILE_CLOSE(fHandle);
      end_if;
    end_if;
    
    if(oK = FALSE) then
      TRACE("RamEx Thread - Error");
    end_if;
    ITASK_SET_STOPWAIT_CNTR(-1);
  end_while;

END_FUNCTION

FUNCTION GLOBAL RAMex::IsAsyncFileOperationInProgress
VAR_OUTPUT
	state 	: UDINT;
END_VAR

  state := 1;
  
  if(MultiTask.GETTASKSTATE(threadParas.hThread) = MTASKSTATE_BLOCKED_GET) then
    state := 0;
  end_if;

END_FUNCTION


FUNCTION GLOBAL RAMex::GetAsyncThreadHandle
VAR_OUTPUT
	hThread 	: MT_TASKHANDLE;
END_VAR

  hThread := threadParas.hThread;
  
END_FUNCTION
