Przeglądaj źródła

Started with support of generic object files for linux based A2.
Kernel boots until new threads have to run.

Requires another small addition in Heaps.Mod that is not ready for commit yet:
[ ProtRecBlockDesc* = RECORD (RecordBlockDesc)
count*: LONGINT;
locked*: BOOLEAN;
awaitingLock*, awaitingCond*: ProcessQueue;
lockedBy*: ANY;
lock*: ANY; (* used by Win32, unused for I386 --> actually not used any more! *)
waitingPriorities*: ARRAY NumPriorities OF LONGINT;

mtx*: ADDRESS; (* processes blocked awaiting lock (UnixAos only) *)
enter*: ADDRESS; (* processes blocked awaiting lock (UnixAos only) *)
END;
]

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@6894 8c9fc860-2736-0410-a75d-ab315db34111

felixf 8 lat temu
rodzic
commit
94f1704c5c

+ 1 - 0
source/FoxCompiler.Mod

@@ -572,4 +572,5 @@ BEGIN
 	AddPlatform("A2G","-b=AMD --objectFile=Generic --newObjectFile --mergeSections --objectFileExtension=.GofG --symbolFileExtension=.SymG");
 	AddPlatform("A2Coop","-b=AMD --cooperative --objectFile=Generic --newObjectFile --traceModule=Trace --mergeSections");
 	AddPlatform("ARMA2","-b=ARM --objectFile=Generic --newObjectFile --symbolFile=Textual --mergeSections");
+	AddPlatform("Linux32G","-b=AMD --objectFile=Generic --newObjectFile --traceModule=Trace --symbolFile=Textual --objectFileExtension=.GofU --symbolFileExtension=.SymU");
 END Compiler.

+ 216 - 0
source/Generic.Linux.I386.Glue.Mod

@@ -0,0 +1,216 @@
+(* Minimal ELF header for self contained linux Oberon programs *)
+(* Copyright (c) Felix Friedrich, ETH Zürich *)
+
+MODULE Glue; 
+
+IMPORT SYSTEM, Trace;
+
+CONST 
+	base = 08048000H;
+	debug* = {0};
+VAR
+	last-: RECORD END; (* empty variable linked to end of kernel *)
+	
+	dlsym-	: PROCEDURE {C} ( handle: ADDRESS; name: ADDRESS): ADDRESS;
+	dlopen-	: PROCEDURE {C} ( pathname: ADDRESS; mode: LONGINT ): ADDRESS;
+	dlclose-	: PROCEDURE {C} ( handle: ADDRESS );
+	exit-		: PROCEDURE {C} (status: LONGINT);
+
+	stackBottom-	: ADDRESS;	(* of main thread *)
+
+
+	PROCEDURE {INITIAL, NOPAF} EntryPoint;
+	CODE
+		; ELF header
+		DB 07FH, 'ELF', 1, 1, 1, 0
+		DD 0, 0
+		DW 02, 03
+		DD 01
+		DD entry + base; program entry point
+		DD elfheadersize
+		DD 0
+		DD 0
+		DW elfheadersize
+		DW 20H
+		DW 3 ; #program header table entries
+		DW 0
+		DW 0
+		DW 0
+
+		elfheadersize:
+
+		; program header
+		DD 1
+		DD 0
+		DD base; 
+		DD base; 
+		DD @last - base; segment size (file)
+		DD @last - base; segment size (memory)
+		DD 07
+		DD 1000H; alignment
+		
+		; interpreter header
+		DD 3
+		DD interpretername; interpreter name offset
+		DD interpretername + base; interpreter name 
+		DD interpretername + base; interpreter name
+		DD interpretername_end - interpretername ; interpreter name length
+		DD interpretername_end - interpretername ; interpreter name length
+		DD 4H
+		DD 1H
+
+		; dynamic header
+		DD 02H
+		DD dynamicsection 
+		DD dynamicsection + base
+		DD dynamicsection + base
+		DD dynamicsection_end - dynamicsection ; size of dynamic section
+		DD dynamicsection_end - dynamicsection ; size of dynamic section
+		DD 06H
+		DD 04H
+
+		dynamicsection:
+		DD 05H, base + stringtable
+		DD 06H, symboltablebegin + base
+		DD 07H, dlsymrelocation + base
+		DD 08H, dlsymrelocation_end-dlsymrelocation ; size (relocationtable)
+		DD 09H, 0CH
+		DD 0AH, stringtable_end - stringtable; size (stringtable)
+		DD 0BH, 10H
+		
+		DD 01H, libname - stringtable; position of libname
+		DD 0H, 0H ; sentinel
+		dynamicsection_end:		
+		
+	 	dlsymrelocation:
+		DD @dlsym
+		DB 01H
+		DB 01H, 00H, 00H; index of the symbol
+		DD 0H
+		dlsymrelocation_end:
+
+		stringtable:
+		DB 0H ; sentinel
+
+		libname:
+		DB 'libdl.so.2', 0
+		
+		dlsymname:
+		DB 'dlsym', 0
+		
+		
+		stringtable_end:
+		
+		ALIGN 4
+		symboltablebegin:
+		DD	0;
+		DD	0
+		DD	0
+		DB	0
+		DB	0
+		DW 0
+		
+		; dlsym symbol
+		DD dlsymname - stringtable; position of dlsymname
+		DD	0
+		DD	0
+		DB	12H ; info: global + function
+		DB 0
+		DW	0
+
+		interpretername:
+		DB '/lib/ld-linux.so.2', 0
+		interpretername_end:
+
+		ALIGN 4
+
+		entry:
+	END EntryPoint;
+
+	(*
+	PROCEDURE {FINAL} ExitPoint;
+	BEGIN
+		Trace.String("exiting"); Trace.Ln;
+		exit(0);
+	END ExitPoint;
+	*)
+
+	PROCEDURE {NOPAF} putc*(file: ADDRESS; c: CHAR);
+	CODE
+		PUSH ECX
+		MOV EAX, 4
+		MOV EBX, [ESP + 12]
+		LEA ECX, [ESP+8]	
+		MOV EDX, 1
+		INT 80H
+		POP ECX
+		JNE fail
+		MOV EAX, [ESP + 4]
+		RET
+		fail:
+		MOV EAX, -1
+		RET
+	END putc;
+
+	PROCEDURE Dlsym*(handle: ADDRESS; CONST name: ARRAY OF CHAR; adr: ADDRESS);
+	VAR val: ADDRESS;
+	BEGIN
+		val := dlsym(handle, ADDRESS OF name[0]);
+		SYSTEM.PUT32(adr, val);
+	END Dlsym;
+
+	PROCEDURE Char(c: CHAR);
+	BEGIN
+		putc(1, c);
+	END Char;
+ 
+	PROCEDURE Init;
+	VAR i: LONGINT;
+	BEGIN
+		Trace.Init;
+		Trace.Char := Char;
+		stackBottom := ADDRESSOF( i ) + 2*SIZEOF(ADDRESS);
+		ASSERT(dlsym # NIL);
+		Dlsym(0,"dlopen", ADDRESS OF dlopen);
+		ASSERT(dlopen # NIL); 
+		Dlsym( 0, "dlclose", ADDRESS OF dlclose);
+		ASSERT(dlclose # NIL); 
+		Dlsym(0,"exit", ADDRESS OF exit);
+		ASSERT(exit # NIL);
+		
+		TRACE(ADDRESS OF last);
+		TRACE(base);
+		TRACE(ADDRESS OF last - base);
+	END Init;
+	
+	PROCEDURE {INITIAL,NOPAF} Init0;
+	BEGIN
+		Init;
+	END Init0;
+
+
+BEGIN
+	Trace.String("Glue loaded"); Trace.Ln;
+END Glue.
+
+SystemTools.FreeDownTo FoxIntermediateBackend ~
+
+SystemTools.DoCommands
+	Compiler.Compile -p=Linux32G
+		Runtime.Mod Trace.Mod Generic.Linux.I386.Glue.Mod Generic.Linux.I386.Unix.Mod Generic.Unix.I386.Machine.Mod Heaps.Mod  Generic.Modules.Mod 
+		Generic.Unix.Objects.Mod  ~ 
+
+	StaticLinker.Link --fileFormat=Raw --fileName=simple_elf --extension=.GofU --displacement=08048000H
+		Runtime Trace Glue Unix Machine Heaps Modules Objects ~
+
+	FSTools.CloseFiles simple_elf ~
+		~ 
+
+#	Release.Build --path="../obg/"  WinAosNewObjectFile ~
+#	StaticLinker.Link --fileFormat=PE32 --fileName=A2.exe --extension=GofW --displacement=401000H --path="../obg/" 
+
+Runtime Trace Kernel32 Machine Heaps Modules Objects Kernel KernelLog 
+Streams Commands FIles WinFS Clock Dates Reals Strings Diagnostics 
+BitSets StringPool ObjectFile GenericLinker Reflection  GenericLoader  BootConsole ~
+
+FoxGenericObjectFile.Show Machine.GofU ~

+ 939 - 0
source/Generic.Linux.I386.Unix.Mod

@@ -0,0 +1,939 @@
+MODULE Unix;   (* Josef Templ, 5.3.90	SVR4 system calls *)  (** Linux x86 version, non portable *)
+
+(* Linux PPC version	g.f. 18.04.98	*)
+(* Linux x86 version	g.f 10.11.99 	*)
+(*	2000.09.19	g.f.	'Sigaltstack' added	*)
+(*	2002.06.27	g.f.	TmDesc fixed,  new: Utime, Mktime *)
+(*	2006.07.31	g.f.	shared memory support added *)
+(*	2007.04.13	g.f.	Thread priorities added *)
+
+IMPORT S := SYSTEM, Glue, Trace;
+
+
+CONST
+	Version* = "Linux";
+
+  	libcname* = "libc.so.6";
+	libmname* = "libm.so.6";
+	libX11name* = "libX11.so.6";
+	libXextname* = "libXext.so.6";
+	libpthreadname = "libpthread.so.0";
+
+	LittleEndian* = TRUE;   (** byte order of basic types *)
+	LsbIs0* = TRUE;   (** bit order of SET type *)
+
+	PageSize* = 4096;	(* least MMU page size *)
+
+	stdin* = 0;  stdout* = 1;  stderr* = 2;
+
+	(** Unix error codes:	*)
+	EPERM* = 1;   (* Not super-user *)
+	ENOENT* = 2;   (* No such file or directory *)
+	ESRCH* = 3;   (* No such process *)
+	EINTR* = 4;   (* interrupted system call *)
+	EIO* = 5;   (* I/O error *)
+	ENXIO* = 6;   (* No such device or address *)
+	E2BIG* = 7;   (* Arg list too long *)
+	ENOEXEC* = 8;   (* Exec format error *)
+	EBADF* = 9;   (* Bad file number *)
+	ECHILD* = 10;   (* No children *)
+	EAGAIN* = 11;   (* No more processes *)
+	ENOMEM* = 12;   (* Not enough core *)
+	EACCES* = 13;   (* Permission denied *)
+	EFAULT* = 14;   (* Bad address *)
+	ENOTBLK* = 15;   (* Block device required *)
+	EBUSY* = 16;   (* Mount device busy *)
+	EEXIST* = 17;   (* File exists *)
+	EXDEV* = 18;   (* Cross-device link *)
+	ENODEV* = 19;   (* No such device *)
+	ENOTDIR* = 20;   (* Not a directory *)
+	EISDIR* = 21;   (* Is a directory *)
+	EINVAL* = 22;   (* Invalid argument *)
+	ENFILE* = 23;   (* File table overflow *)
+	EMFILE* = 24;   (* Too many open files *)
+	ENOTTY* = 25;   (* Inappropriate ioctl for device *)
+	ETXTBSY* = 26;   (* Text file busy *)
+	EFBIG* = 27;   (* File too large *)
+	ENOSPC* = 28;   (* No space left on device *)
+	ESPIPE* = 29;   (* Illegal seek *)
+	EROFS* = 30;   (* Read only file system *)
+	EMLINK* = 31;   (* Too many links *)
+	EPIPE* = 32;   (* Broken pipe *)
+	EDOM* = 33;   (* Math arg out of domain of func *)
+	ERANGE* = 34;   (* Math result not representable *)
+	ENOMSG* = 42;   (* No message of desired type *)
+	EIDRM* = 43;   (* Identifier removed *)
+	ECHRNG* = 44;   (* Channel number out of range *)
+	EL2NSYNC* = 45;   (* Level 2 not synchronized *)
+	EL3HLT* = 46;   (* Level 3 halted *)
+	EL3RST* = 47;   (* Level 3 reset *)
+	ELNRNG* = 48;   (* Link number out of range *)
+	EUNATCH* = 49;   (* Protocol driver not attached *)
+	ENOCSI* = 50;   (* No CSI structure available *)
+	EL2HLT* = 51;   (* Level 2 halted *)
+	EDEADLK* = 35;   (* Deadlock condition. *)
+	ENOLCK* = 37;   (* No record locks available. *)
+
+	(* open flags *)
+	rdonly* = {};  rdwr* = {1};  creat* = {6};  trunc* = {9};
+
+	(* access modes *)
+	rwrwr* = {2, 4, 5, 7, 8};  rwxrwxrwx* = {0..8};
+	
+	F_OK* = {};  X_Ok* = {0};  W_OK* = {1};  R_OK* = {2};
+
+	SCHED_OTHER = 0;
+	SIGUSR1 = 10;
+	SIGUSR2 = 12;
+
+	T_SIGRESUME = SIGUSR1;
+	T_SIGSUSPEND = SIGUSR2; 
+		
+	(*--------------------------- Threads -------------------------------*)
+
+TYPE
+	Thread_t* = ADDRESS;
+	Mutex_t* = ADDRESS;
+	MutexType = ARRAY 8 OF ADDRESS; 
+	(*
+	Mutex = ARRAY 8 OF ADDRESS;
+	Mutex_t* = POINTER TO Mutex;
+	*)
+	Condition_t* = ADDRESS;
+	ConditionType = ARRAY 12 OF WORD;
+	
+CONST
+	(* Thread priorities *)
+	ThreadLow* = 1; ThreadNormal* = 20; ThreadHigh* = 99;	
+	NumPriorities* = 99;	(* number of priority levels *)
+
+
+	(*---------------------------- IP -----------------------------------*)
+
+CONST
+	(* domain *)
+		AFINET* = 2;
+		AFINET6* = 26;
+		
+		PFINET* = AFINET;
+		PFINET6* = AFINET6;
+		
+	(* types of connection*)
+		SockStream*	= 1;
+		SockDGram*	= 2;
+		
+	(* Protocols *)
+		IpProtoUDP*	= 17;
+		IpProtoTCP*		=  6;
+
+	(* setsockopt *)
+		SoLSocket*		= 1;			(* socket option level *)
+		SoLinger* 		= 13; 		(* linger (gracefully close the connection)*)
+		SoKeepAlive*	= 9;			(* keep connection alive *)
+		SoNoDelay*	= 1;			(* no delay *)
+
+	(* ioctl *)
+		FioNRead*		= 541BH;	(* something to read ? *)
+		
+	(* recv *)
+		MsgPeek*		= 2;
+		MsgDontWait*	= 40H;		(* non blocking read *)
+		
+	(* shutdown *)
+		ShutRDWR*	= 2;
+
+	SockAddrSizeV4*	= 16;
+	SockAddrSizeV6*	= 28;
+
+	(*----------------------- seral line, cf Unix.V24.Mod -----------------------*)
+	
+	FIONREAD*	= 541BH;
+	TIOCMGET*		= 5415H;
+	TIOCMBIS*		= 5416H;
+	TIOCMBIC*		= 5417H;
+	TIOCMSET*		= 5418H;
+	
+	(* speeds *)
+	B0*	= 0;
+	B50*	= 1;
+	B75*	= 2; 
+	B110*	= 3; 
+	B134*	= 4; 
+	B150*	= 5; 
+	B200*	= 6; 
+	B300*	= 7; 
+	B600*	= 8; 
+	B1200* 	= 9; 
+	B1800* 	= 10; 
+	B2400* 	= 11; 
+	B4800* 	= 12; 
+	B9600* 	= 13; 
+	B19200*	= 14; 
+	B38400*	= 15; 
+
+	B57600* 	= 1001H; 
+	B115200*	= 1002H; 
+	B230400*	= 1003H; 
+
+
+
+	(* tio.cflag bits *)
+	CSIZE*		= {4,5};
+	   CS5*		= {};
+	   CS6*		= {4};
+	   CS7*		= {5};
+	   CS8*		= {4,5};
+	CSTOPB*	= {6};
+	CREAD*	= {7};
+	PARENB*	= {8};
+	PARODD*	= {9};
+	HUPCL*		= {10};
+	CLOCAL*	= {11};
+	
+TYPE
+	Termios* = RECORD
+		iflags*, oflags*, cflags*, lflags*: SET;
+		line*: CHAR;
+		cc*	: ARRAY 32 OF CHAR;
+		ispeed-, ospeed-: LONGINT
+	END;
+
+	(*------------------------------------------------------------------*)
+
+
+TYPE
+	DevT* = HUGEINT;
+
+	Status* = RECORD
+				dev-		: DevT;
+				unused1-	: LONGINT;
+				ino-		: LONGINT;
+				mode-	: LONGINT;
+				nlink-		: LONGINT;
+				uid-		: LONGINT;
+				gid-		: LONGINT;
+				rdev-		: DevT;
+				unused2-	: LONGINT;
+				size-		: LONGINT;
+				blksize-	: LONGINT;
+				blocks-	: LONGINT;
+				atime-	: Timeval;
+				mtime-	: Timeval;
+				ctime-	: Timeval;
+				unused-	: ARRAY 2 OF LONGINT;
+			END;
+
+	Timeval* = RECORD
+				sec*	: LONGINT;
+				usec*	: LONGINT
+			END;
+
+	TmPtr* = POINTER TO Tm;
+	Tm* = RECORD
+				sec*, min*, hour*, mday*, mon*, year*, wday*, yday*, isdst*: LONGINT;
+				gmtoff*, tmzone*: LONGINT;
+			END;
+
+	Tms* = POINTER TO RECORD
+				utime*, stime*, cutime*, cstime*: LONGINT
+			END;
+
+	Timezone* = RECORD
+				minuteswest*, dsttime*: LONGINT
+			END;
+
+	Itimerval* = RECORD
+				interval*, value*: Timeval
+			END;
+
+
+CONST
+	FdSetLen* = 32;
+	
+TYPE
+	FdSet* = ARRAY FdSetLen OF SET;
+
+
+	Dirent* = POINTER TO RECORD
+				ino-, off-	: LONGINT;   (* these may change to HUGEINT in future Linux releases!! *)
+				reclen-	: INTEGER;
+				typ-		: CHAR;
+				name-	: (*array of*) CHAR;
+			END;
+
+	Stack* = RECORD
+				sp*		: ADDRESS;
+				flags*		: SET;
+				size*		: SIZE
+			END;
+
+	Ucontext* = POINTER TO UcontextDesc;
+	UcontextDesc* = RECORD
+				flags-		: SET;
+				link-		: ADDRESS;
+				stack-	: Stack;
+				mc-		: McontextDesc
+			END;
+
+	Mcontext* = POINTER TO McontextDesc;
+	McontextDesc* = RECORD
+				r_gs-		: LONGINT;
+				r_fs-		: LONGINT;
+				r_es-		: LONGINT;
+				r_ds-		: LONGINT;
+				r_di-		: LONGINT;
+				r_si-		: LONGINT;
+				r_bp-		: LONGINT;
+				r_sp_ -	: LONGINT;
+				r_bx-		: LONGINT;
+				r_dx-		: LONGINT;
+				r_cx-		: LONGINT;
+				r_ax-		: LONGINT;
+				r_trapno-	: LONGINT;
+				r_err-		: LONGINT;
+				r_pc-		: LONGINT;
+				r_cs-		: LONGINT;
+				r_flags-	: LONGINT;
+				r_sp-		: LONGINT;		(* sp at error (signal) *)
+				r_ss-		: LONGINT;
+				fpc-		: ADDRESS; 	(* FPcontext *)
+				oldmask-	: LONGINT;
+				cr2-		: LONGINT
+			END;
+
+	FPcontext* = POINTER TO FPcontextDesc;
+	FPcontextDesc* = RECORD
+				(* ... *)
+			END;
+
+	PThreadAttr = RECORD
+		reserved1-: ARRAY 5 OF ADDRESS;
+		reserved2-: ARRAY 4 OF WORD;
+	END;
+
+VAR
+	argc-: LONGINT;  argv-: ADDRESS;
+	aargc: ADDRESS;
+
+	sysinfo-: RECORD
+				sysname-, nodename-, release-, version-, machine-: ARRAY 65 OF CHAR;
+			END;
+
+	pthread_mutex_init: PROCEDURE {C} (mutex: ADDRESS; mutexattr: ADDRESS): WORD;
+	pthread_mutex_destroy: PROCEDURE {C} (mutex: ADDRESS): WORD;
+	pthread_mutex_lock: PROCEDURE {C} (mutex: ADDRESS): WORD;
+	pthread_mutex_unlock: PROCEDURE {C} (mutex: ADDRESS): WORD;
+
+	pthread_cond_init: PROCEDURE {C} (cond: ADDRESS; condAttr: ADDRESS): WORD;
+	pthread_cond_destroy: PROCEDURE {C} (cond: ADDRESS): WORD;
+	pthread_cond_wait: PROCEDURE {C} (cond: ADDRESS; mutex: ADDRESS): WORD;
+	pthread_cond_signal: PROCEDURE {C} (cond: ADDRESS): WORD; 
+	
+	pthread_create: PROCEDURE {C} (newthread: ADDRESS; attr: ADDRESS; start_routine: PROCEDURE {C} (arg: ADDRESS); arg: ADDRESS): WORD;
+	pthread_exit: PROCEDURE {C} (return: ADDRESS);
+
+	pthread_attr_init: PROCEDURE {C} (attr: ADDRESS);
+	pthread_attr_setscope: PROCEDURE {C}(attr: ADDRESS; set: WORD);
+	pthread_attr_setdetachstate: PROCEDURE {C}(attr: ADDRESS; set: WORD);
+	pthread_attr_setstacksize: PROCEDURE {C}(attr: ADDRESS; stackSize: SIZE);
+	pthread_self: PROCEDURE {C} (): Thread_t;
+
+	sched_get_priority_max: PROCEDURE {C} (policy: LONGINT): LONGINT;
+	sched_get_priority_min: PROCEDURE {C} (policy: LONGINT): LONGINT;
+	pthread_setschedparam: PROCEDURE {C} (thread: Thread_t; policy: LONGINT; param: ADDRESS): WORD;
+	pthread_getschedparam: PROCEDURE {C} (thread: Thread_t; policy: LONGINT; param: ADDRESS): WORD;
+	
+	sigaction: PROCEDURE {C} (signum: LONGINT; CONST act, oldact: ADDRESS): LONGINT;
+	sigemptyset: PROCEDURE {C} (set: ADDRESS);
+	sigaddset: PROCEDURE {C} (set: ADDRESS; signo: LONGINT);
+	nanosleep: PROCEDURE {C} (req, rem: ADDRESS): LONGINT;
+
+	
+	read-			: PROCEDURE {C} ( fd: LONGINT; buf: ADDRESS; n: SIZE ): LONGINT;
+	write-			: PROCEDURE {C} ( fd: LONGINT; buf: ADDRESS; n: SIZE ): LONGINT;
+	open-			: PROCEDURE {C} ( name: ADDRESS;  flags, mode: SET ): LONGINT;
+	close-		: PROCEDURE {C} ( fd: LONGINT ): LONGINT;
+	lseek-		: PROCEDURE {C} ( fd: LONGINT; offset, origin: SIZE ): LONGINT;
+	fsync-		: PROCEDURE {C} ( fd: LONGINT ): LONGINT;
+	ioctl-			: PROCEDURE {C} ( fd: LONGINT; request: LONGINT; arg: ADDRESS ): LONGINT;
+	unlink-		: PROCEDURE {C} ( name: ADDRESS ): LONGINT;
+	rename-		: PROCEDURE {C} ( oldname, newname: ADDRESS ): LONGINT;
+	ftruncate-		: PROCEDURE {C} ( fd: LONGINT;  length: SIZE ): LONGINT;
+	chmod-		: PROCEDURE {C} ( name: ADDRESS;  mode: SET ): LONGINT;
+	utime-		: PROCEDURE {C} ( name: ADDRESS;  tb: ADDRESS ): LONGINT;
+	access-		: PROCEDURE {C} ( name: ADDRESS;  mode: SET ): LONGINT;
+	select-		: PROCEDURE {C} ( width: LONGINT; rd, wr, ex: ADDRESS;  VAR timeout: Timeval ): LONGINT;
+
+	chdir-			: PROCEDURE {C} ( name: ADDRESS ): LONGINT;
+	mkdir-		: PROCEDURE {C} ( name: ADDRESS;  mode: SET ): LONGINT;
+	rmdir-			: PROCEDURE {C} ( path: ADDRESS ): LONGINT;
+
+	stat-			: PROCEDURE {C} ( name: ADDRESS;  VAR buf: Status ): LONGINT;
+	lstat-			: PROCEDURE {C} ( name: ADDRESS;  VAR buf: Status ): LONGINT;
+	fstat-			: PROCEDURE {C} ( fd: LONGINT;  VAR buf: Status ): LONGINT;
+
+	getpid-		: PROCEDURE {C} ( ): LONGINT;
+	getuid-		: PROCEDURE {C} ( ): LONGINT;
+
+	malloc-		: PROCEDURE {C} ( size: SIZE ): ADDRESS;
+	posix_memalign-	: PROCEDURE {C} ( VAR p: ADDRESS;  alignment, size: SIZE ): LONGINT;
+	free-			: PROCEDURE {C} ( p: ADDRESS );
+	mprotect-	: PROCEDURE {C} ( p: ADDRESS; len: SIZE; prot: LONGINT ): LONGINT;
+
+	alarm-		: PROCEDURE {C} ( ms: LONGINT ): LONGINT;
+	setitimer-		: PROCEDURE {C} ( which: LONGINT;  VAR value, ovalue: Itimerval ): LONGINT;
+	getitimer-		: PROCEDURE {C} ( which: LONGINT;  VAR value: Itimerval ): LONGINT;
+
+	gettimeofday-	: PROCEDURE {C} ( VAR tv: Timeval;  VAR tz: Timezone ): LONGINT;
+	mktime-			: PROCEDURE {C} ( VAR tm: Tm ): LONGINT;
+	localtime-		: PROCEDURE {C} ( CONST tv: Timeval ): TmPtr;
+	time-				: PROCEDURE {C} ( VAR tv: Timeval ): LONGINT;
+	times-			: PROCEDURE {C} ( VAR tms: Tms ): LONGINT;
+
+	system-		: PROCEDURE {C} ( cmd: ADDRESS );
+	uname-		: PROCEDURE {C} ( utsname: ADDRESS ): LONGINT;
+
+	getcwd-		: PROCEDURE {C} ( buf: ADDRESS;  len: SIZE ): ADDRESS;
+	getenv-		: PROCEDURE {C} ( name: ADDRESS ): ADDRESS;
+
+	opendir-		: PROCEDURE {C} ( name: ADDRESS ): ADDRESS;
+	readdir-		: PROCEDURE {C} ( dir: ADDRESS ): Dirent;
+	closedir-		: PROCEDURE {C} ( dir: ADDRESS );
+
+	sigsetjmp-	: PROCEDURE {C} ( env: ADDRESS;  savemask: LONGINT ): LONGINT;
+	siglongjmp-	: PROCEDURE {C} ( env: ADDRESS;  val: LONGINT );
+
+	kill-			: PROCEDURE {C} ( pid, sig: LONGINT ): LONGINT;
+	exit-			: PROCEDURE {C} ( status: LONGINT );
+	perror-		: PROCEDURE {C} ( msg: ADDRESS );
+	errno-			: PROCEDURE {C} ( ): LONGINT;
+
+
+	libc-: LONGINT;
+	libp-: LONGINT;
+
+	libraryPaths: ARRAY 8 OF ARRAY 32 OF CHAR;
+	noPaths: LONGINT;
+
+
+
+
+
+
+	(*
+	mtxInit: 		PROCEDURE {REALTIME, C}  ( dummy: LONGINT ): Unix.Mutex_t;
+	mtxDestroy: 	PROCEDURE {REALTIME, C}  ( mtx: Unix.Mutex_t );
+	mtxLock: 		PROCEDURE {REALTIME, C}  ( mtx: Unix.Mutex_t );
+	mtxUnlock:	PROCEDURE {REALTIME, C}  ( mtx: Unix.Mutex_t );
+
+	conInit: 		PROCEDURE {REALTIME, C}  ( dummy: LONGINT ): Unix.Condition_t;
+	conDestroy:	PROCEDURE {REALTIME, C}  ( cond: Unix.Condition_t );
+	conWait: 		PROCEDURE {REALTIME, C}  ( cond: Unix.Condition_t;  mtx: Unix.Mutex_t );
+	conSignal: 	PROCEDURE {REALTIME, C}  ( cond: Unix.Condition_t );
+	
+	thrStart: 			PROCEDURE {REALTIME, C} ( p: PROCEDURE;  stackLen: LONGINT ): Unix.Thread_t;
+	thrThis: 			PROCEDURE {REALTIME, C} ( dummy: LONGINT ): Unix.Thread_t;
+	thrSleep: 			PROCEDURE {REALTIME, C} ( ms: LONGINT );
+	thrYield: 			PROCEDURE {REALTIME, C} ( dummy: LONGINT );
+	thrExit: 			PROCEDURE {REALTIME, C} ( dummy: LONGINT );
+	thrSuspend: 		PROCEDURE {REALTIME, C} ( t: Unix.Thread_t );
+	thrResume: 		PROCEDURE {REALTIME, C} ( t: Unix.Thread_t );
+	thrSetPriority: 	PROCEDURE {REALTIME, C} ( t: Unix.Thread_t;  prio: LONGINT );
+	thrGetPriority: 	PROCEDURE {REALTIME, C} ( t: Unix.Thread_t ): LONGINT;
+	thrKill: 			PROCEDURE {REALTIME, C} ( t: Unix.Thread_t );
+	*)
+	
+	TYPE 
+
+	PROCEDURE MtxInit*(dummy: LONGINT): Mutex_t;
+	VAR 
+		mtx: Mutex_t;
+	BEGIN
+		mtx := malloc(SIZEOF(MutexType));
+		TRACE(mtx);
+		ASSERT(mtx # 0);
+		ASSERT(pthread_mutex_init(mtx, NIL) = 0);
+		TRACE(mtx);
+		RETURN mtx;
+	END MtxInit;
+	
+	PROCEDURE MtxDestroy*(mtx: Mutex_t);
+	BEGIN
+		ASSERT(pthread_mutex_destroy(mtx) = 0);
+	END MtxDestroy;
+
+	PROCEDURE MtxLock*(mtx: Mutex_t);
+	BEGIN
+		ASSERT(pthread_mutex_lock(mtx) = 0);
+	END MtxLock;
+
+	PROCEDURE MtxUnlock*(mtx: Mutex_t);
+	BEGIN
+		ASSERT(pthread_mutex_unlock(mtx) = 0);
+	END MtxUnlock;
+	
+	PROCEDURE ConInit* (dummy: LONGINT): Condition_t;
+	VAR cond: Condition_t;
+	BEGIN
+		cond := malloc(SIZEOF(ConditionType));
+		ASSERT(cond # 0);
+		ASSERT(pthread_cond_init(cond, NIL)=0);
+		RETURN cond;
+	END ConInit;
+	
+	PROCEDURE ConDestroy*(con: Condition_t);
+	BEGIN
+		ASSERT(pthread_cond_destroy(con) = 0);
+	END ConDestroy;
+
+	PROCEDURE ConWait*(con: Condition_t; mtx: Mutex_t);
+	BEGIN
+		ASSERT(pthread_cond_wait(con, mtx) = 0);
+	END ConWait;
+
+	PROCEDURE ConSignal*(cond: Condition_t);
+	BEGIN
+		ASSERT(pthread_cond_signal(cond) = 0);
+	END ConSignal;
+	
+	PROCEDURE ThrThis*(dummy: LONGINT): Thread_t;
+	BEGIN
+		RETURN pthread_self();
+	END ThrThis;
+
+	PROCEDURE ThrYield*(dummy: LONGINT);
+	BEGIN
+		ThrSleep(1);
+	END ThrYield;
+
+	PROCEDURE ThrExit*(dummy: LONGINT);
+	BEGIN
+		pthread_exit(0);
+	END ThrExit;
+	
+	PROCEDURE ThrSetPriority*(thr: Thread_t; prio: LONGINT);
+	VAR 
+		param: Sched_param;
+		policy: LONGINT;
+	BEGIN
+	    IF pthread_getschedparam( thr, ADDRESS OF policy, ADDRESS OF param ) # 0 THEN END;
+	    param.sched_priority := prio;
+	    IF pthread_setschedparam( thr, SCHED_OTHER, ADDRESS OF param ) # 0 THEN
+	    	Perror("pthread_setschedparam");
+	    END
+	END ThrSetPriority;
+
+	PROCEDURE ThrGetPriority*(thr: Thread_t):LONGINT;
+	VAR 
+		param: Sched_param;
+		policy: LONGINT;
+	BEGIN
+	    IF pthread_getschedparam( thr, ADDRESS OF policy, ADDRESS OF param ) # 0 THEN END;
+	    RETURN  param.sched_priority;
+	END ThrGetPriority;
+	
+	PROCEDURE {C} Starter(arg: ADDRESS);
+	BEGIN
+		TRACE("starter has started");
+	END Starter;
+	
+	PROCEDURE ThrStart*(p: PROCEDURE; stackSize: SIZE): Thread_t;
+	VAR 
+		attr: PThreadAttr;
+		id: Thread_t;
+	CONST 
+		PTHREAD_SCOPE_SYSTEM = 0;
+		PTHREAD_CREATE_DETACHED = 1;
+	
+	BEGIN
+		pthread_attr_init(ADDRESS OF attr);
+		pthread_attr_setscope(ADDRESS OF attr, PTHREAD_SCOPE_SYSTEM);
+		pthread_attr_setdetachstate(ADDRESS OF attr, PTHREAD_CREATE_DETACHED);
+		pthread_attr_setstacksize(ADDRESS OF attr, stackSize);
+		ASSERT(pthread_create(ADDRESS OF id, ADDRESS OF attr, Starter, p) = 0);
+	END ThrStart;
+
+	PROCEDURE ThrSleep*(ms: LONGINT);
+	VAR
+		sltime,rem: RECORD
+			tv_sec: WORD;        (* seconds *)
+            tv_nsec: WORD;       (* nanoseconds *)
+         END;
+	BEGIN
+		sltime.tv_sec := ms DIV 1000;
+		sltime.tv_nsec := 1000000*(ms MOD 1000);
+		WHILE (nanosleep(ADDRESS OF sltime, ADDRESS OF rem) < 0) DO
+			(* check error ! *)
+			sltime := rem;
+		END;
+	END ThrSleep;
+	
+	PROCEDURE ThrKill*(thr: Thread_t);
+	BEGIN
+	(*
+    if (thr != mainthread) {
+    	pthread_detach( thr );
+    	if (thr == pthread_self())
+    	    pthread_exit( 0 );
+    	else {
+    	    pthread_cancel( thr );
+        } 
+    }
+    *)
+    END ThrKill;
+	
+	
+PROCEDURE {C} resume_handler(sig: LONGINT);
+BEGIN
+END resume_handler;
+
+PROCEDURE {C} suspend_handler(sig: LONGINT);
+BEGIN
+	TRACE("suspend handler"); 
+	(*
+    sigset_t block;
+
+    sigfillset( &block );
+    sigdelset( &block, T_SIGRESUME );
+    suspend_done = 1;
+
+    sigsuspend( &block ); /* await T_SIGRESUME */
+
+    resume_done = 1;
+    *)
+END suspend_handler;
+
+PROCEDURE ThrResume*(thr: Thread_t);
+VAR n: LONGINT;
+BEGIN
+	(*
+	void 
+    int n;
+
+    pthread_mutex_lock( &suspend_mutex );
+    resume_done = 0; n = 1;
+    pthread_kill( thr, T_SIGRESUME ); 
+    while (resume_done != 1 && n < 50) { o_thrSleep( 1 ); n++; }
+    pthread_mutex_unlock( &suspend_mutex );
+}
+*)
+END ThrResume;
+
+
+PROCEDURE ThrSuspend*(thr: Thread_t);
+VAR n: LONGINT;
+BEGIN
+	(*
+PROCEDURE ThrResume*(thr: Thread_t);
+VAR n: LONGINT;
+BEGIN
+	(*
+	void 
+    int n;
+
+    pthread_mutex_lock( &suspend_mutex );
+    resume_done = 0; n = 1;
+    pthread_kill( thr, T_SIGRESUME ); 
+    while (resume_done != 1 && n < 50) { o_thrSleep( 1 ); n++; }
+    pthread_mutex_unlock( &suspend_mutex );
+}
+*)
+END ThrResume;
+
+
+}
+*)
+END ThrSuspend;
+
+
+TYPE
+	Sigaction = RECORD
+		sa_handler: ADDRESS;
+		sa_mask: HUGEINT; 
+		sa_flags: ADDRESS;
+		filler: ADDRESS;
+	END;
+	
+	Sched_param = RECORD
+		sched_priority: LONGINT;
+		safety: ARRAY 8 OF ADDRESS; 
+	END;
+	
+VAR 
+	suspend_mutex: MutexType;
+	mainthread: Thread_t;
+	sasuspend, saresume: Sigaction;
+
+	PROCEDURE ThrInitialize*(VAR low, high: LONGINT): BOOLEAN;
+	VAR
+		param: Sched_param;
+	BEGIN
+	    (*struct sched_param param;*)
+	    
+		ASSERT(pthread_mutex_init( ADDRESS OF suspend_mutex, NIL ) = 0);
+	    mainthread := pthread_self();
+	    TRACE(mainthread);
+	    high := sched_get_priority_max(SCHED_OTHER);
+	    low := sched_get_priority_min(SCHED_OTHER);
+	    TRACE(low, high);
+	    
+	    param.sched_priority := high;
+		IF pthread_setschedparam( mainthread, SCHED_OTHER, ADDRESS OF param)#0 THEN
+			Perror("ThrInitialize: setparam");
+		END;
+		
+		
+	    (*
+	    sigemptyset( ADDRESS OF sasuspend.sa_mask );
+	    sigaddset(  ADDRESS OF sasuspend.sa_mask, T_SIGRESUME );
+	    sasuspend.sa_flags := 0;
+	    sasuspend.sa_handler := suspend_handler;
+	    ASSERT(sigaction( T_SIGSUSPEND, ADDRESS OF sasuspend, NIL ) = 0);
+
+	    sigemptyset( ADDRESS OF saresume.sa_mask );
+	    saresume.sa_flags := 0;
+	    saresume.sa_handler := resume_handler;
+	    ASSERT(sigaction( T_SIGRESUME, ADDRESS OF saresume, NIL ) = 0);
+	    *)
+	    RETURN TRUE;
+	END ThrInitialize;
+
+
+	PROCEDURE ModifyContext*( cont: Ucontext;  pc, bp, sp: LONGINT );
+	BEGIN
+		cont.mc.r_pc := pc;
+		cont.mc.r_bp := bp;
+		cont.mc.r_sp := sp;
+	END ModifyContext;
+
+	PROCEDURE Perror*( CONST msg: ARRAY OF CHAR );
+	BEGIN
+		perror( ADDRESSOF( msg ) )
+	END Perror;
+
+	PROCEDURE Dlsym*( lib: ADDRESS;  CONST sym: ARRAY OF CHAR;  objAddr: ADDRESS );
+	VAR val: LONGINT;
+	BEGIN
+		Glue.Dlsym( lib, sym, objAddr );
+		S.GET( objAddr, val );
+		IF val = 0 THEN
+			Trace.String( "Unix.Dlsym:  entry '" );  Trace.String( sym );  Trace.String( "' not found" );
+			Trace.Ln
+		ELSE
+			Trace.String( "Unix.Dlsym found " );  Trace.String( sym ); 
+			Trace.Ln
+		END
+	END Dlsym;
+
+	PROCEDURE Dlopen*( CONST libname: ARRAY OF CHAR;  mode: LONGINT ): ADDRESS;
+	VAR h: ADDRESS;  i, j, k: INTEGER;
+		p: ARRAY 256 OF CHAR;
+	BEGIN
+		IF libname[0] = '/' THEN  h := Glue.dlopen( ADDRESSOF( libname ), mode );
+		ELSE
+			i := 0;  h := 0;
+			WHILE (h = 0) & (i < noPaths) DO
+				COPY( libraryPaths[i], p );  j := 0;
+				WHILE p[j] # 0X DO  INC( j )  END;
+				p[j] := '/';  k := 0;
+				REPEAT  INC( j );  p[j] := libname[k];  INC( k )  UNTIL p[j] = 0X;
+				h := Glue.dlopen( ADDRESSOF( p ), mode );  INC( i )
+			END
+		END;
+		IF h = 0 THEN
+			Trace.String( "Unix.Dlopen: loading library " );
+			Trace.String( libname );  Trace.String( " failed" );  Trace.Ln;
+			LOOP END;
+		END;
+		RETURN h
+	END Dlopen;
+
+	PROCEDURE Dlclose*( lib: ADDRESS );
+	BEGIN
+		Glue.dlclose( lib )
+	END Dlclose;
+
+	PROCEDURE GetArg*( no: LONGINT;  VAR val: ARRAY OF CHAR );
+	VAR ch: CHAR;  adr: ADDRESS;  i: LONGINT;
+	BEGIN
+		IF no >= argc THEN  val[0] := 0X
+		ELSE
+			S.GET( argv + SIZEOF(ADDRESS)*no, adr );  i := 0;
+			REPEAT  S.GET( adr, ch );  val[i] := ch;  INC( adr );  INC( i );   UNTIL (ch = 0X) OR (i >= LEN( val ));
+		END
+	END GetArg;
+
+	PROCEDURE GetArgval*(  CONST argName: ARRAY OF CHAR;  VAR val: ARRAY OF CHAR );
+	VAR i: INTEGER;
+		buf: ARRAY 40 OF CHAR;
+	BEGIN
+		i := 1;
+		WHILE i < argc - 1 DO
+			GetArg( i, buf );
+			IF buf = argName THEN  GetArg( i + 1, val );  RETURN   END;
+			INC( i )
+		END;
+		val[0] := 0X
+	END GetArgval;
+
+
+	PROCEDURE getSysinfo;
+	VAR res: LONGINT;  p: INTEGER;
+		buf: ARRAY 4096 OF CHAR;
+
+		PROCEDURE copy( VAR p: INTEGER;  VAR dest: ARRAY OF CHAR );
+		VAR i: INTEGER;
+		BEGIN
+			WHILE buf[p] <= ' ' DO  INC( p )  END;
+			i := 0;
+			REPEAT  dest[i] := buf[p];  INC( i );  INC( p )  UNTIL (buf[p - 1] = 0X) OR (i >= LEN( dest ));
+			dest[i - 1] := 0X
+		END copy;
+
+	BEGIN
+		FOR p := 0 TO 4096 - 1 DO  buf[p] := 0X  END;
+		res := uname( ADDRESSOF( buf ) );
+		p := 0;
+		copy( p, sysinfo.sysname );
+		copy( p, sysinfo.nodename );
+		copy( p, sysinfo.release );
+		copy( p, sysinfo.version );
+		copy( p, sysinfo.machine );
+	END getSysinfo;
+	
+	PROCEDURE Init;
+	VAR test: ADDRESS; 
+	BEGIN
+		TRACE("Unix init start");
+		Dlsym( 0, "argc", ADDRESSOF( aargc ) );  
+		IF aargc # NIL THEN
+			S.GET( aargc, argc );
+		END;
+		Dlsym( 0, "argv", ADDRESSOF( argv ) );
+
+		libraryPaths[0] := "/lib/i386-linux-gnu";
+		libraryPaths[1] := "/usr/lib/i386-linux-gnu";
+		libraryPaths[2] := "/lib";
+		libraryPaths[3] := "/usr/lib";
+		noPaths := 4;
+
+		libc := Dlopen( libcname, 2 );
+		libp := Dlopen( libpthreadname, 2); 
+		
+
+		Dlsym( libc, "pthread_mutex_init", ADDRESSOF(pthread_mutex_init));
+		Dlsym( libc, "pthread_mutex_destroy", ADDRESSOF(pthread_mutex_destroy));
+		Dlsym( libc, "pthread_mutex_lock", ADDRESSOF(pthread_mutex_lock));
+		Dlsym( libc, "pthread_mutex_unlock", ADDRESSOF(pthread_mutex_unlock));
+		
+		Dlsym( libc, "pthread_cond_init", ADDRESSOF(pthread_cond_init));
+		Dlsym( libc, "pthread_cond_destroy", ADDRESSOF(pthread_cond_destroy));
+		Dlsym( libc, "pthread_cond_wait", ADDRESSOF(pthread_cond_wait));
+		Dlsym( libc, "pthread_cond_signal", ADDRESSOF(pthread_cond_signal));
+
+		Dlsym( libp, "pthread_create", ADDRESSOF(pthread_create));
+		Dlsym( libc, "pthread_exit", ADDRESSOF(pthread_exit));
+
+		Dlsym( libp, "pthread_attr_init", ADDRESSOF(pthread_attr_init));
+		Dlsym( libp, "pthread_attr_setscope", ADDRESSOF(pthread_attr_setscope));
+		Dlsym( libp, "pthread_attr_setdetachstate", ADDRESSOF(pthread_attr_setdetachstate));
+		Dlsym( libp, "pthread_attr_setstacksize", ADDRESSOF(pthread_attr_setstacksize));
+		Dlsym( libp, "pthread_self", ADDRESSOF(pthread_self));
+		Dlsym( libc, "sched_get_priority_max", ADDRESSOF(sched_get_priority_max));
+		Dlsym( libc, "sched_get_priority_min", ADDRESSOF(sched_get_priority_min));
+		Dlsym( libc, "pthread_setschedparam", ADDRESSOF(pthread_setschedparam));
+		Dlsym( libc, "sigaction", ADDRESSOF(sigaction));
+		Dlsym( libc, "sigemptyset", ADDRESSOF(sigemptyset));
+		Dlsym( libc, "sigaddset", ADDRESSOF(sigaddset));
+		Dlsym( libc, "nanosleep", ADDRESSOF(nanosleep));
+				
+				
+				
+		Dlsym( libc, "putc", ADDRESSOF(test));
+
+		
+		Dlsym( libc, "sigaltstack", ADDRESSOF(test));
+		Dlsym( libc, "sigaddset", ADDRESSOF(test));
+		Dlsym( libc, "sigaction", ADDRESSOF(test));
+		Dlsym( libc, "sigfillset", ADDRESSOF(test));
+		Dlsym( libc, "sigdelset", ADDRESSOF(test));
+		Dlsym( libc, "sigsuspend", ADDRESSOF(test));
+		Dlsym( libc, "pthread_detach", ADDRESSOF(test));
+		Dlsym( libc, "pthread_sigmask", ADDRESSOF(test));
+		Dlsym( libc, "pthread_setcancelstate", ADDRESSOF(test));
+		Dlsym( libc, "pthread_setcanceltype", ADDRESSOF(test));
+		Dlsym( libc, "pthread_getschedparam", ADDRESSOF(test));
+		Dlsym( libc, "pthread_setschedparam", ADDRESSOF(test));
+		Dlsym( libc, "pthread_cancel", ADDRESSOF(test));
+		Dlsym( libc, "pthread_create", ADDRESSOF(test));
+		Dlsym( libc, "pthread_kill", ADDRESSOF(test));
+		
+		Dlsym( libc, "read",		ADDRESSOF( read ) );
+		Dlsym( libc, "write",	ADDRESSOF( write ) );
+		Dlsym( libc, "open",		ADDRESSOF( open ) );
+		Dlsym( libc, "close",	ADDRESSOF( close ) );
+		Dlsym( libc, "lseek",	ADDRESSOF( lseek ) );
+		Dlsym( libc, "fsync",	ADDRESSOF( fsync ) );
+		Dlsym( libc, "ioctl",	ADDRESSOF( ioctl ) );
+		Dlsym( libc, "unlink",	ADDRESSOF( unlink ) );
+		Dlsym( libc, "rename",	ADDRESSOF( rename ) );
+		Dlsym( libc, "ftruncate",	ADDRESSOF( ftruncate ) );
+		Dlsym( libc, "chmod",	ADDRESSOF( chmod ) );
+		Dlsym( libc, "utime",	ADDRESSOF( utime ) );
+		Dlsym( libc, "access",	ADDRESSOF( access ) );
+		Dlsym( libc, "select",	ADDRESSOF( select ) );
+
+		Dlsym( libc, "chdir",	ADDRESSOF( chdir ) );
+		Dlsym( libc, "mkdir",	ADDRESSOF( mkdir ) );
+		Dlsym( libc, "rmdir",	ADDRESSOF( rmdir ) );
+
+		Dlsym( libc, "stat",		ADDRESSOF( stat ) );
+		Dlsym( libc, "lstat",	ADDRESSOF( lstat ) );
+		Dlsym( libc, "fstat",	ADDRESSOF( fstat ) );
+
+		Dlsym( libc, "getpid",	ADDRESSOF( getpid ) );
+		Dlsym( libc, "getuid",	ADDRESSOF( getuid ) );
+
+		Dlsym( libc, "alarm",	ADDRESSOF( alarm ) );
+		Dlsym( libc, "setitimer",	ADDRESSOF( setitimer ) );
+		Dlsym( libc, "getitimer",	ADDRESSOF( getitimer ) );
+
+		Dlsym( libc, "gettimeofday", ADDRESSOF( gettimeofday ) );
+		Dlsym( libc, "mktime",		ADDRESSOF( mktime ) );
+		Dlsym( libc, "localtime",	ADDRESSOF( localtime ) );
+		Dlsym( libc, "time",			ADDRESSOF( time ) );
+		Dlsym( libc, "times",		ADDRESSOF( times ) );
+
+		Dlsym( libc, "getcwd",		ADDRESSOF( getcwd ) );
+		Dlsym( libc, "getenv",		ADDRESSOF( getenv ) );
+
+		Dlsym( libc, "opendir",		ADDRESSOF( opendir ) );
+		Dlsym( libc, "readdir",		ADDRESSOF( readdir ) );
+		Dlsym( libc, "closedir",	ADDRESSOF( closedir ) );
+
+		Dlsym( libc, "sigsetjmp",	ADDRESSOF( sigsetjmp ) );
+		Dlsym( libc, "siglongjmp",	ADDRESSOF( siglongjmp ) );
+
+		Dlsym( libc, "malloc",		ADDRESSOF( malloc ) );
+		Dlsym( libc, "posix_memalign",	ADDRESSOF( posix_memalign ) );
+		Dlsym( libc, "free",			ADDRESSOF( free ) );
+		Dlsym( libc, "mprotect",	ADDRESSOF( mprotect ) );
+
+		Dlsym( libc, "system",	ADDRESSOF( system ) );
+		Dlsym( libc, "uname",	ADDRESSOF( uname ) );
+
+		Dlsym( libc, "kill",		ADDRESSOF( kill ) );
+		Dlsym( libc, "exit",		ADDRESSOF( exit ) );
+		Dlsym( libc, "perror",	ADDRESSOF( perror ) );
+		Dlsym( libc, "errno",	ADDRESSOF( errno ) );
+
+		getSysinfo;
+		
+		TRACE("Unix init end");
+	END Init;
+
+	PROCEDURE {INITIAL} Init0;
+	BEGIN
+		Init;
+	END Init0;
+END Unix.

+ 1042 - 0
source/Generic.Unix.I386.Machine.Mod

@@ -0,0 +1,1042 @@
+MODULE Machine;	(** AUTHOR "pjm, G.F."; PURPOSE "Bootstrapping, configuration and machine interface"; *)
+
+IMPORT S := SYSTEM, Trace, Unix, Glue;
+
+CONST
+	DefaultConfig = "Color 0  StackSize 128";
+	
+	Version = "Aos (rev.6640)";
+
+	DefaultObjectFileExtension* = ".Obj";
+	
+	Second* = 1000; (* frequency of ticks increments in Hz *)
+
+	(** bits in features variable *)
+	MTTR* = 12;  MMX* = 23; 
+	
+	AddrSize = SIZEOF( ADDRESS );
+	SizeSize = SIZEOF( SIZE );
+	
+	AddressSize = SIZEOF(ADDRESS);
+	BlockHeaderSize = 2 * AddressSize;
+	RecordDescSize = 4 * AddressSize;  (* needs to be adapted in case Heaps.RecordBlockDesc is changed *)
+	StaticBlockSize = 32;		(* static heap block size *)
+
+	BlockSize = 32;
+	MemBlockSize* = 64*1024*1024;
+	
+	TraceOutput* = 0;	(* Trace output *)
+	Memory* = 1;		(*!  Virtual memory management, stack and page allocation,  not used in UnixAos *)
+	Heaps* = 2;   		(* Storage allocation and Garbage collection *)
+	Interrupts* = 3;		(*!  Interrupt handling,  not used in UnixAos *)
+	Modules* = 4;		(* Module list *)
+	Objects* = 5;		(*!  Ready queue,  not used in UnixAos *)
+	Processors* = 6;	(*!  Interprocessor interrupts,  not used in UnixAos *)
+	KernelLog* = 7;		(* Atomic output *)
+	X11* = 8;				(* XWindows I/O *)
+	Trap* = 9;
+	GC = 10;
+	MaxLocks* = 11;   (* { <= 32 } *)
+	
+	MaxCPU* = 4;
+	
+
+TYPE	
+	Vendor* = ARRAY 13 OF CHAR;	
+
+	MemoryBlock* = POINTER TO MemoryBlockDesc;
+	MemoryBlockDesc* = RECORD
+		next- {UNTRACED}: MemoryBlock;
+		startAdr-: ADDRESS; 		(* sort key in linked list of memory blocks *)
+		size-: SIZE; 					
+		beginBlockAdr-, endBlockAdr-: ADDRESS
+	END;
+	
+	(** processor state, ordering of record fields is predefined! *)
+		(*!(not used in UnixAos, for interface compatibility only)*)
+	State* = RECORD					(* offsets used in FieldInterrupt, FieldIRQ and Objects.RestoreState *)
+		EDI*, ESI*, ERR*, ESP0*, EBX*, EDX*, ECX*, EAX*: LONGINT;	(** ESP0 = ADR(s.INT) *)
+		INT*, BP*, PC*, CS*: LONGINT;	(* BP and ERR are exchanged by glue code, for procedure link *)
+		FLAGS*: SET;
+		SP*, SS*: LONGINT;			(** only valid if (VMBit IN s.EFLAGS) OR (CS MOD 4 < s.CS MOD 4) *)
+		ES*, DS*, FS*, GS*: LONGINT;	(** only valid if (VMBit IN s.FLAGS) *)
+	END;
+	
+	
+VAR
+	mtx	: ARRAY MaxLocks OF Unix.Mutex_t;
+	
+	version-: ARRAY 64 OF CHAR;	(** Aos version *)
+	
+	features-, features2 : SET;
+	MMXSupport-	: BOOLEAN;
+	SSESupport-	: BOOLEAN;
+	SSE2Support-	: BOOLEAN;
+	SSE3Support-	: BOOLEAN;
+	SSSE3Support-	: BOOLEAN;
+	SSE41Support-	: BOOLEAN;
+	SSE42Support-	: BOOLEAN;
+	SSE5Support-	: BOOLEAN;
+	AVXSupport-		: BOOLEAN;
+	
+	GCacquired- : BOOLEAN;
+	
+	ticks-: LONGINT;	(** timer ticks. Use Kernel.GetTicks() to read, don't write *)
+	
+	prioLow-, prioHigh-: LONGINT;	(* permitted thread priorities *)
+	
+	fcr-: SET;	(** default floating-point control register value (default rounding mode is towards -infinity, for ENTIER) *)
+	mhz-: HUGEINT;	(** clock rate of GetTimer in MHz, or 0 if not known *)
+	
+	standaloneAppl-: BOOLEAN;
+	
+	firstMemBlock: MemoryBlockDesc;		(* pseudo heap *)
+	
+	gcThreshold-: SIZE;
+	memBlockHead-{UNTRACED}, memBlockTail-{UNTRACED}: MemoryBlock; (* head and tail of sorted list of memory blocks *)
+	
+	config: ARRAY 2048 OF CHAR;	(* config strings *)
+	
+	(*
+	Unix.ThrInitialize	: PROCEDURE {REALTIME, C} ( VAR low, high: LONGINT ): BOOLEAN;
+	
+	Unix.MtxInit			: PROCEDURE {REALTIME, C} ( dummy: LONGINT ): Unix.Mutex_t;
+	Unix.MtxDestroy	: PROCEDURE {REALTIME, C} ( mtx: Unix.Mutex_t );
+	Unix.MtxLock		: PROCEDURE {REALTIME, C} ( mtx: Unix.Mutex_t );
+	Unix.MtxUnlock	: PROCEDURE {REALTIME, C} ( mtx: Unix.Mutex_t );
+	
+	conInit			: PROCEDURE {REALTIME, C}  ( dummy: LONGINT ): Unix.Condition_t;
+	conDestroy	: PROCEDURE {REALTIME, C}  ( cond: Unix.Condition_t );
+	conWait		: PROCEDURE {REALTIME, C}  ( cond: Unix.Condition_t;  mtx: Unix.Mutex_t );
+	conSignal		: PROCEDURE {REALTIME, C}  ( cond: Unix.Condition_t );
+	
+	thrSleep		: PROCEDURE {REALTIME, C} ( ms: LONGINT );
+	thrThis			: PROCEDURE {REALTIME, C} ( dummy: LONGINT ): Unix.Thread_t;
+	*)
+	
+	logfile: LONGINT;
+	traceHeap: BOOLEAN;
+
+	timer0	: HUGEINT;
+
+	(** Return current processor ID (0 to MaxNum-1). *)
+	PROCEDURE {REALTIME} ID* (): LONGINT;
+	BEGIN
+		RETURN 0
+	END ID;
+
+	(**
+	 * Flush Data Cache for the specified virtual address range. If len is negative, flushes the whole cache.
+	 * This is used on some architecture to interact with DMA hardware (e.g. Ethernet and USB. It can be
+	 * left empty on Intel architecture.
+	 *)
+	PROCEDURE FlushDCacheRange * (adr: ADDRESS; len: LONGINT);
+	END FlushDCacheRange;
+
+	(**
+	 * Invalidate Data Cache for the specified virtual address range. If len is negative, flushes the whole cache.
+	 * This is used on some architecture to interact with DMA hardware (e.g. Ethernet and USB. It can be
+	 * left empty on Intel architecture.
+	 *)
+	PROCEDURE InvalidateDCacheRange * (adr: ADDRESS; len: LONGINT);
+	END InvalidateDCacheRange;
+
+	(**
+	 * Invalidate Instruction Cache for the specified virtual address range. If len is negative, flushes the whole cache.
+	 * This is used on some architecture to interact with DMA hardware (e.g. Ethernet and USB. It can be
+	 * left empty on Intel architecture.
+	 *)
+	PROCEDURE InvalidateICacheRange * (adr: ADDRESS; len: LONGINT);
+	END InvalidateICacheRange;
+	
+	(* insert given memory block in sorted list of memory blocks, sort key is startAdr field - called during GC *)
+	PROCEDURE InsertMemoryBlock(memBlock: MemoryBlock);
+	VAR cur {UNTRACED}, prev {UNTRACED}: MemoryBlock;
+	BEGIN
+		cur := memBlockHead;
+		prev := NIL;
+		WHILE (cur # NIL) & (cur.startAdr < memBlock.startAdr) DO
+			prev := cur;
+			cur := cur.next
+		END;
+		IF prev = NIL THEN (* insert at head of list *)
+			memBlock.next := memBlockHead;
+			memBlockHead := memBlock
+		ELSE (* insert in middle or at end of list *)
+			prev.next := memBlock;
+			memBlock.next := cur;
+			IF cur = NIL THEN
+				memBlockTail := memBlock
+			END
+		END
+	END InsertMemoryBlock;
+
+		
+	(* Free unused memory block - called during GC *)
+	PROCEDURE FreeMemBlock*(memBlock: MemoryBlock);
+	VAR cur {UNTRACED}, prev {UNTRACED}: MemoryBlock;
+	BEGIN
+		cur := memBlockHead;
+		prev := NIL;
+		WHILE (cur # NIL) & (cur # memBlock) DO
+			prev := cur;
+			cur := cur.next
+		END;
+		IF cur = memBlock THEN 
+			IF traceHeap THEN  
+				Trace.String( "Release memory block " );  Trace.Hex( memBlock.startAdr, -8 );  Trace.Ln
+			END;
+			IF prev = NIL THEN
+				memBlockHead := cur.next
+			ELSE
+				prev.next := cur.next;
+				IF cur.next = NIL THEN
+					memBlockTail := prev
+				END
+			END;
+			Unix.free( memBlock.startAdr )
+		ELSE
+			HALT(535)	(* error in memory block management *)
+		END;
+	END FreeMemBlock;
+
+	
+
+	(* expand heap by allocating a new memory block *)
+	PROCEDURE ExpandHeap*( dummy: LONGINT; size: SIZE; VAR memoryBlock: MemoryBlock; VAR beginBlockAdr, endBlockAdr: ADDRESS );
+	VAR mBlock: MemoryBlock;  alloc, s: SIZE;  a, adr: ADDRESS; 
+	BEGIN 
+		IF size < (MemBlockSize - (2*BlockSize))  THEN  alloc := MemBlockSize  
+		ELSE  alloc := size + (2*BlockSize);
+		END;  
+		INC( alloc, (-alloc) MOD Unix.PageSize );
+		
+		IF Unix.posix_memalign( adr, Unix.PageSize, alloc ) # 0 THEN
+			Unix.Perror( "Machine.ExpandHeap: posix_memalign" );
+			beginBlockAdr := 0;
+			endBlockAdr := 0
+		ELSE
+			IF Unix.mprotect( adr, alloc, 7 (* READ WRITE EXEC *) ) # 0 THEN
+				Unix.Perror( "Machine.ExpandHeap: mprotect" )
+			END;
+			
+			mBlock := S.VAL( MemoryBlock, adr );  
+			mBlock.next := NIL;  
+			mBlock.startAdr := adr;
+			mBlock.size := alloc;  
+			mBlock.beginBlockAdr := adr + BlockSize - AddrSize;  
+			
+			ASSERT( (mBlock.beginBlockAdr + AddrSize) MOD BlockSize = 0 );  
+
+			s := adr + alloc - mBlock.beginBlockAdr - BlockSize; 
+			DEC( s, s MOD BlockSize );  
+			ASSERT( s >= size );  
+			mBlock.endBlockAdr := mBlock.beginBlockAdr + s; 
+			
+			InsertMemoryBlock( mBlock );
+			IF traceHeap THEN TraceHeap( mBlock )  END;
+			
+			a := mBlock.beginBlockAdr;
+			S.PUT( a, a + AddrSize );	(* tag *)
+			S.PUT( a + AddrSize, s - AddrSize );  (* size *)
+			S.PUT( a + AddrSize + SizeSize, S.VAL( ADDRESS, 0 ) ); (* next *)
+			
+			beginBlockAdr := mBlock.beginBlockAdr;
+			endBlockAdr := mBlock.endBlockAdr;
+			memoryBlock := mBlock;
+		END  
+	END ExpandHeap;
+		
+	(* Set memory block end address *)
+	PROCEDURE SetMemoryBlockEndAddress*(memBlock: MemoryBlock; endBlockAdr: ADDRESS);
+	BEGIN
+		ASSERT(endBlockAdr >= memBlock.beginBlockAdr);
+		memBlock.endBlockAdr := endBlockAdr
+	END SetMemoryBlockEndAddress;
+
+	PROCEDURE TraceHeap( new: MemoryBlock );
+	VAR cur{UNTRACED}: MemoryBlock;
+	BEGIN
+		Trace.Ln;
+		Trace.String( "Heap expanded" );  Trace.Ln;
+		cur := memBlockHead;
+		WHILE cur # NIL DO
+			Trace.Hex( cur.startAdr, -8 );  Trace.String( "   " );  Trace.Int( cur.size, 15 );
+			IF cur = new THEN  Trace.String( "  (new)" )  END;
+			Trace.Ln;
+			cur := cur.next
+		END
+	END TraceHeap;
+
+	(** Get first memory block and first free address, the first free address is identical to memBlockHead.endBlockAdr *)
+	PROCEDURE GetStaticHeap*(VAR beginBlockAdr, endBlockAdr, freeBlockAdr: ADDRESS);
+	VAR memBlockAdr: ADDRESS;
+	BEGIN
+		InitHeap(memBlockHead,beginBlockAdr, endBlockAdr);
+		memBlockTail := memBlockHead;
+
+		(*
+		SYSTEM.GET(bootHeapAdr + EndBlockOfs, freeBlockAdr);
+		ASSERT(freeBlockAdr MOD StaticBlockSize = 0);
+		memBlockAdr := bootHeapAdr + HeaderSize + MemoryBlockOfs;
+
+
+		memBlockHead := SYSTEM.VAL(MemoryBlock, memBlockAdr); (* this block will never be freed since there is a global reference (initBlock in Heaps.Mod) to it *)
+		memBlockHead.startAdr := bootHeapAdr;
+		memBlockHead.size := bootHeapSize;
+		ASSERT(memBlockHead.beginBlockAdr MOD StaticBlockSize = 0);
+		ASSERT((memBlockHead.endBlockAdr - memBlockHead.beginBlockAdr) MOD StaticBlockSize = 0);
+		memBlockTail := memBlockHead;
+		*)
+		beginBlockAdr := memBlockHead.beginBlockAdr;
+		endBlockAdr := memBlockHead.endBlockAdr;
+		freeBlockAdr := beginBlockAdr;
+	END GetStaticHeap;
+
+	(* returns if an address is a currently allocated heap address *)
+	PROCEDURE ValidHeapAddress*( p: ADDRESS ): BOOLEAN;
+	VAR mb: MemoryBlock; 
+	BEGIN
+		mb := memBlockHead;  
+		WHILE mb # NIL DO
+			IF (p >= mb.beginBlockAdr) & (p <= mb.endBlockAdr) THEN  RETURN TRUE  END;  
+			mb := mb.next;  
+		END;  
+		RETURN FALSE  
+	END ValidHeapAddress;
+	
+
+	(** Return information on free memory in Kbytes. *)
+	PROCEDURE GetFreeK*(VAR total, lowFree, highFree: SIZE);
+	BEGIN
+		(*! meaningless in Unix port, for interface compatibility only *)
+		total := 0;
+		lowFree := 0;
+		highFree := 0
+	END GetFreeK;
+	
+	
+
+	(** Fill "size" bytes at "destAdr" with "filler". "size" must be multiple of 4. *)
+	PROCEDURE {REALTIME} Fill32* (destAdr: ADDRESS; size: SIZE; filler: LONGINT);
+	CODE {SYSTEM.i386}
+		MOV EDI, [EBP+destAdr]
+		MOV ECX, [EBP+size]
+		MOV EAX, [EBP+filler]
+		TEST ECX, 3
+		JZ ok
+		PUSH 8	; ASSERT failure
+		INT 3
+	ok:
+		SHR ECX, 2
+		CLD
+		REP STOSD
+	END Fill32;
+
+
+PROCEDURE  Portin8*(port: LONGINT; VAR val: CHAR);
+END Portin8;
+
+PROCEDURE  Portin16*(port: LONGINT; VAR val: INTEGER);
+END Portin16;
+
+PROCEDURE  Portin32*(port: LONGINT; VAR val: LONGINT);
+END Portin32;
+
+PROCEDURE  Portout8*(port: LONGINT; val: CHAR);
+END Portout8;
+
+PROCEDURE  Portout16*(port: LONGINT; val: INTEGER);
+END Portout16;
+
+PROCEDURE  Portout32*(port: LONGINT; val: LONGINT);
+END Portout32;
+
+
+(** -- Atomic operations -- *)
+
+(** Atomic INC(x). *)
+PROCEDURE -AtomicInc*(VAR x: LONGINT);
+CODE {SYSTEM.i386}
+	POP EAX
+	LOCK
+	INC DWORD [EAX]
+END AtomicInc;
+
+(** Atomic DEC(x). *)
+PROCEDURE -AtomicDec*(VAR x: LONGINT);
+CODE {SYSTEM.i386}
+	POP EAX
+	LOCK
+	DEC DWORD [EAX]
+END AtomicDec;
+
+(** Atomic INC(x, y). *)
+PROCEDURE -AtomicAdd*(VAR x: LONGINT; y: LONGINT);
+CODE {SYSTEM.i386}
+	POP EBX
+	POP EAX
+	LOCK
+	ADD DWORD [EAX], EBX
+END AtomicAdd;
+
+
+(** Atomic EXCL. *)
+PROCEDURE AtomicExcl* (VAR s: SET; bit: LONGINT);
+CODE {SYSTEM.i386}
+	MOV EAX, [EBP+bit]
+	MOV EBX, [EBP+s]
+	LOCK
+	BTR [EBX], EAX
+END AtomicExcl;
+
+
+(** Atomic test-and-set. Set x = TRUE and return old value of x. *)
+PROCEDURE -AtomicTestSet*(VAR x: BOOLEAN): BOOLEAN;
+CODE {SYSTEM.i386}
+	POP EBX
+	MOV AL, 1
+	XCHG [EBX], AL
+END AtomicTestSet;
+
+(* Atomic compare-and-swap. Set x = new if x = old and return old value of x *)
+PROCEDURE {REALTIME} -AtomicCAS* (VAR x: LONGINT; old, new: LONGINT): LONGINT;
+CODE {SYSTEM.i386}
+	POP EBX		; new
+	POP EAX		; old
+	POP ECX		; address of x
+	DB 0F0X, 00FX, 0B1X, 019X	; LOCK CMPXCHG [ECX], EBX; atomicly compare x with old and set it to new if equal
+END AtomicCAS;
+
+
+(* Return current instruction pointer *)
+PROCEDURE {REALTIME} CurrentPC* (): ADDRESS;
+CODE {SYSTEM.i386}
+	MOV EAX, [EBP+4]
+END CurrentPC;
+
+(* Return current frame pointer *)
+PROCEDURE {REALTIME} -CurrentBP* (): ADDRESS;
+CODE {SYSTEM.i386}
+	MOV EAX, EBP
+END CurrentBP;
+
+(* Set current frame pointer *)
+PROCEDURE {REALTIME} -SetBP* (bp: ADDRESS);
+CODE {SYSTEM.i386}
+	POP EBP
+END SetBP;
+
+(* Return current stack pointer *)
+PROCEDURE {REALTIME} -CurrentSP* (): ADDRESS;
+CODE {SYSTEM.i386}
+	MOV EAX, ESP
+END CurrentSP;
+
+(* Set current stack pointer *)
+PROCEDURE {REALTIME} -SetSP* (sp: ADDRESS);
+CODE {SYSTEM.i386}
+	POP ESP
+END SetSP;
+
+PROCEDURE {REALTIME} -GetEAX*(): LONGINT;
+CODE{SYSTEM.i386}
+END GetEAX;
+
+PROCEDURE {REALTIME} -GetECX*(): LONGINT;
+CODE{SYSTEM.i386}
+	MOV EAX,ECX	
+END GetECX;
+
+PROCEDURE {REALTIME} -GetESI*(): LONGINT;
+CODE{SYSTEM.i386}
+	MOV EAX,ESI	
+END GetESI;
+
+PROCEDURE {REALTIME} -GetEDI*(): LONGINT;
+CODE{SYSTEM.i386}
+	MOV EAX,EDI	
+END GetEDI;
+
+
+PROCEDURE {REALTIME} -SetEAX*(n: LONGINT);
+CODE{SYSTEM.i386}	
+	POP EAX
+END SetEAX;
+
+PROCEDURE {REALTIME} -SetEBX*(n: LONGINT);
+CODE{SYSTEM.i386}
+	POP EBX
+END SetEBX;
+
+PROCEDURE {REALTIME} -SetECX*(n: LONGINT);
+CODE{SYSTEM.i386}
+	POP ECX
+END SetECX;
+
+PROCEDURE {REALTIME} -SetEDX*(n: LONGINT);
+CODE{SYSTEM.i386}
+	POP EDX
+END SetEDX;
+
+PROCEDURE {REALTIME} -SetESI*(n: LONGINT);
+CODE{SYSTEM.i386}
+	POP ESI
+END SetESI;
+
+PROCEDURE {REALTIME} -SetEDI*(n: LONGINT);
+CODE{SYSTEM.i386}
+	POP EDI
+END SetEDI;
+
+
+PROCEDURE -GetTimer* (): HUGEINT;
+CODE {SYSTEM.Pentium}
+	RDTSC	; set EDX:EAX
+END GetTimer;
+
+
+	(** -- Configuration and bootstrapping -- *)
+
+	(** Return the value of the configuration string specified by parameter name in parameter val. Returns val = "" if the string was not found, or has an empty value. *)
+	PROCEDURE GetConfig* (CONST name: ARRAY OF CHAR; VAR val: ARRAY OF CHAR);
+	VAR i, src: LONGINT; ch: CHAR;
+	BEGIN
+		ASSERT (name[0] # "=");	(* no longer supported, use GetInit instead *)
+		src := -1;
+		LOOP
+			REPEAT
+				INC( src );  ch := config[src]; 
+				IF ch = 0X THEN EXIT END;
+			UNTIL ch > ' ';
+			i := 0;
+			LOOP
+				ch := config[src];
+				IF (ch # name[i]) OR (name[i] = 0X) THEN EXIT END;
+				INC (i); INC (src)
+			END;
+			IF (ch <= ' ') & (name[i] = 0X) THEN	(* found *)
+				i := 0;
+				REPEAT
+					INC (src); ch := config[src]; val[i] := ch; INC (i);
+					IF i = LEN(val) THEN val[i - 1] := 0X; RETURN END	(* val too short *)
+				UNTIL ch <= ' ';
+				IF ch = ' ' THEN val[i -1] := 0X END; 
+				RETURN
+			ELSE
+				WHILE ch > ' ' DO	(* skip to end of name *)
+					INC (src); ch := config[src]
+				END;
+				INC (src);
+				REPEAT	(* skip to end of value *)
+					ch := config[src]; INC (src)
+				UNTIL ch <= ' '
+			END
+		END;
+		val[0] := 0X
+	END GetConfig;
+
+
+	(** Convert a string to an integer. Parameter i specifies where in the string scanning should begin (usually 0 in the first call). Scanning stops at the first non-valid character, and i returns the updated position. Parameter s is the string to be scanned. The value is returned as result, or 0 if not valid. Syntax: number = ["-"] digit {digit} ["H" | "h"] . digit = "0" | ... "9" | "A" .. "F" | "a" .. "f" . If the number contains any hexdecimal letter, or if it ends in "H" or "h", it is interpreted as hexadecimal. *)
+	PROCEDURE StrToInt* (VAR i: LONGINT; CONST s: ARRAY OF CHAR): LONGINT;
+	VAR vd, vh, sgn, d: LONGINT; hex: BOOLEAN;
+	BEGIN
+		vd := 0; vh := 0; hex := FALSE;
+		IF s[i] = "-" THEN sgn := -1; INC (i) ELSE sgn := 1 END;
+		LOOP
+			IF (s[i] >= "0") & (s[i] <= "9") THEN d := ORD (s[i])-ORD ("0")
+			ELSIF (CAP (s[i]) >= "A") & (CAP (s[i]) <= "F") THEN d := ORD (CAP (s[i]))-ORD ("A") + 10; hex := TRUE
+			ELSE EXIT
+			END;
+			vd := 10*vd + d; vh := 16*vh + d;
+			INC (i)
+		END;
+		IF CAP (s[i]) = "H" THEN hex := TRUE; INC (i) END;	(* optional H *)
+		IF hex THEN vd := vh END;
+		RETURN sgn * vd
+	END StrToInt;
+	
+
+	(* function returning the number of processors that are available to Aos *)
+	PROCEDURE NumberOfProcessors*( ): LONGINT;
+	BEGIN
+		RETURN 1
+	END NumberOfProcessors;
+
+	(*! non portable code, for native Aos only *)
+	PROCEDURE SetNumberOfProcessors*( num: LONGINT );
+	BEGIN
+		(* numberOfProcessors := num; *)
+	END SetNumberOfProcessors;
+
+	(* function for changing byte order *)
+	PROCEDURE ChangeByteOrder* (n: LONGINT): LONGINT;
+	CODE { SYSTEM.i486 }
+		MOV EAX, [EBP+n]				; load n in eax
+		BSWAP EAX						; swap byte order
+	END ChangeByteOrder;
+
+
+	(* Send and print character *)
+	PROCEDURE TraceChar *(c: CHAR);
+	BEGIN
+		Trace.Char( c )
+	END TraceChar;
+
+
+	(** CPU identification *)
+
+	PROCEDURE CPUID*( VAR vendor: Vendor;  VAR version: LONGINT;  VAR features1,features2: SET );
+	CODE {SYSTEM.i386, SYSTEM.Pentium}
+		MOV	EAX, 0
+		CPUID
+		CMP	EAX, 0
+		JNE	ok
+		MOV	ESI, [EBP+vendor]
+		MOV	[ESI], AL	;  AL = 0
+		MOV	ESI, [EBP+version]
+		MOV	[ESI], EAX	;  EAX = 0
+		MOV	ESI, [EBP+features1]
+		MOV	[ESI], EAX
+		MOV	ESI, [EBP+features2]
+		MOV	[ESI], EAX
+		JMP	end
+	ok:
+		MOV	ESI, [EBP+vendor]
+		MOV	[ESI], EBX
+		MOV	[ESI+4], EDX
+		MOV	[ESI+8], ECX
+		MOV	BYTE [ESI+12], 0
+		MOV	EAX, 1
+		CPUID
+		MOV	ESI, [EBP+version]
+		MOV	[ESI], EAX
+		MOV	ESI, [EBP+features1]
+		MOV	[ESI], EDX
+		MOV	ESI, [EBP+features2]
+		MOV	[ESI], ECX
+	end:
+	END CPUID;
+	
+
+	(* If the CPUID instruction is supported, the ID flag (bit 21) of the EFLAGS register is r/w *)
+	PROCEDURE CpuIdSupported( ) : BOOLEAN;
+	CODE {SYSTEM.i386}	
+		PUSHFD				; save EFLAGS
+		POP EAX				; store EFLAGS in EAX
+		MOV EBX, EAX		; save EBX for later testing
+		XOR EAX, 00200000H	; toggle bit 21
+		PUSH EAX				; push to stack
+		POPFD					; save changed EAX to EFLAGS
+		PUSHFD				; push EFLAGS to TOS
+		POP EAX				; store EFLAGS in EAX
+		CMP EAX, EBX		; see if bit 21 has changed
+		SETNE AL;			; return TRUE if bit 21 has changed, FALSE otherwise
+	END CpuIdSupported;
+
+	
+	(* setup MMX, SSE and SSE2..SSE5 and AVX extension *)
+
+	PROCEDURE SetupSSE2Ext;
+	CONST
+		MMXFlag=23;(*IN features from EBX*)
+		FXSRFlag = 24;
+		SSEFlag = 25;
+		SSE2Flag = 26;
+		SSE3Flag = 0; (*IN features2 from ECX*) (*PH 04/11*)
+		SSSE3Flag =9;
+		SSE41Flag =19;
+		SSE42Flag =20;
+		SSE5Flag = 11;
+		AVXFlag = 28;
+	BEGIN
+		MMXSupport := MMXFlag IN features;
+		SSESupport := SSEFlag IN features;
+		SSE2Support := SSESupport & (SSE2Flag IN features);
+		SSE3Support := SSE2Support & (SSE3Flag IN features2);
+		SSSE3Support := SSE3Support & (SSSE3Flag IN features2); (* PH 04/11*)
+		SSE41Support := SSE3Support & (SSE41Flag IN features2);
+		SSE42Support := SSE3Support & (SSE42Flag IN features2);
+		SSE5Support := SSE3Support & (SSE5Flag IN features2);
+		AVXSupport := SSE3Support & (AVXFlag IN features2);
+
+		IF SSESupport & (FXSRFlag IN features) THEN
+			(* InitSSE(); *) (*! not privileged mode in Windows and Unix, not allowed *)
+		END;
+	END SetupSSE2Ext;
+	
+
+	(** -- Processor initialization -- *)
+	PROCEDURE -SetFCR( s: SET );
+	CODE {SYSTEM.i386, SYSTEM.FPU}
+		FLDCW	[ESP]	;  parameter s
+		POP	EAX
+	END SetFCR;
+
+	PROCEDURE -FCR( ): SET;
+	CODE {SYSTEM.i386, SYSTEM.FPU}
+		PUSH	0
+		FNSTCW	[ESP]
+		FWAIT
+		POP	EAX
+	END FCR;
+
+	PROCEDURE -InitFPU;
+	CODE {SYSTEM.i386, SYSTEM.FPU}
+		FNINIT
+	END InitFPU;
+
+	(** Setup FPU control word of current processor. *)
+	PROCEDURE SetupFPU*;
+	BEGIN
+		InitFPU;  SetFCR( fcr )
+	END SetupFPU;
+
+
+	(* Initialize locks. *)
+	PROCEDURE InitLocks;  
+	VAR i: LONGINT;  a: ADDRESS;
+	BEGIN 
+		i := 0;  
+		WHILE i < MaxLocks DO  
+			TRACE(i);
+		mtx[i] := Unix.MtxInit(0);  INC( i )  END;   
+	END InitLocks;  
+
+	PROCEDURE CleanupLocks*;  
+	VAR i: LONGINT;  
+	BEGIN 
+		i := 0;  
+		WHILE i < MaxLocks DO  Unix.MtxDestroy( mtx[i] );  INC( i ) END;  	
+	END CleanupLocks;  
+	
+	(** Acquire a spin-lock. *)
+	PROCEDURE  Acquire*( level: LONGINT );   (* non reentrant lock *)
+	BEGIN 
+		Unix.MtxLock( mtx[level] );
+	END Acquire;  
+
+	(** Release a spin-lock. *)
+	PROCEDURE  Release*( level: LONGINT );   
+	BEGIN 
+		Unix.MtxUnlock( mtx[level] );
+	END Release;  
+	
+	PROCEDURE  AcquireGC*( ): BOOLEAN;  
+	VAR res: BOOLEAN;
+	BEGIN 
+		Unix.MtxLock( mtx[GC] );
+		IF ~GCacquired THEN  GCacquired := TRUE;  res := TRUE  ELSE  res := FALSE  END;
+		Unix.MtxUnlock( mtx[GC] );
+		RETURN  res
+	END AcquireGC;  
+
+	(** Release a spin-lock. *)
+	PROCEDURE  ReleaseGC*;   
+	BEGIN 
+		Unix.MtxLock( mtx[GC] );
+		GCacquired := FALSE;
+		Unix.MtxUnlock( mtx[GC] )
+	END ReleaseGC;  
+	
+	PROCEDURE Shutdown*( reboot: BOOLEAN );
+	VAR ignore: LONGINT;
+	BEGIN
+		ignore := Unix.close( logfile );
+		IF reboot THEN  Unix.exit( 0 )  ELSE  Unix.exit( 1 )  END;
+	END Shutdown;
+		
+
+		
+
+	(* Set machine-dependent parameter gcThreshold *)
+	PROCEDURE SetGCParams*;
+	BEGIN
+		gcThreshold := 10*1024*1024; (* 10 MB *)
+	END SetGCParams;
+
+	(* expand heap by allocating a new memory block - called during GC *)
+	PROCEDURE InitHeap(VAR memoryBlock: MemoryBlock; VAR beginBlockAdr, endBlockAdr: ADDRESS);
+	CONST MemBlockHeaderSize = BlockHeaderSize + RecordDescSize + BlockHeaderSize;
+		TypeDescOffset = -AddressSize; (* see Heaps.Mod *)
+		HeapBlockOffset = - 2 * AddressSize; (* see Heaps.Mod *)
+		DataAdrOffset = AddressSize; (* offset of dataAdr field in Heaps.HeapBlockDesc *)
+	VAR memDescSize, memBlkSize, alignOffset: SIZE; adr, memHeaderAdr, memBlockAdr, memBlockHeadAdr: ADDRESS;
+		memBlock {UNTRACED}: MemoryBlock; i: LONGINT; ch: CHAR; h: HUGEINT; size: LONGINT;
+		initVal: LONGINT;
+	BEGIN
+
+		(*
+			HeapBlockPtr -- bootHeapAdr
+		4	Type
+		8	Mark
+		12	DataAdr
+		16	Size
+		20	HeapBlockPtr
+		24	Type
+		28	next  -- MemoryBlock
+		32	startAdr
+		36	size
+		40	beginBlockAdr
+		44	endBlockAdr
+		48		--beginBlockAdr
+		....
+				--endBlockAdr
+
+		*)
+		size := 1;
+		memDescSize := MemBlockHeaderSize + SIZEOF(MemoryBlockDesc);
+		INC(memDescSize, (-memDescSize) MOD StaticBlockSize); 	(* round up to multiple of StaticBlockSize *)
+		INC(size, (-size) MOD StaticBlockSize); (* round up to multiple of StaticBlockSize *)
+		memBlkSize := memDescSize + size + StaticBlockSize; 		(* add StaticBlockSize to account for alignments different from multiples of StaticBlockSize *)
+		IF memBlkSize < MemBlockSize THEN memBlkSize := MemBlockSize END; 	(* MemBlockSize implicitly multiple of StaticBlockSize *)
+
+		initVal := 8*1024*1024;
+		TRACE(initVal, adr);
+		IF Unix.posix_memalign( adr, Unix.PageSize, initVal ) # 0 THEN
+			Unix.Perror( "Machine.ExpandHeap: posix_memalign" );
+			beginBlockAdr := 0;
+			endBlockAdr := 0
+		ELSE
+			TRACE(adr, initVal);
+			IF Unix.mprotect( adr, initVal, 7 (* READ WRITE EXEC *) ) # 0 THEN
+				Unix.Perror( "Machine.ExpandHeap: mprotect" )
+			END;
+			TRACE(adr, initVal);
+		END;
+		(*
+		adr := Kernel32.VirtualAlloc(initVal, memBlkSize, {Kernel32.MEMCommit, Kernel32.MEMReserve}, {Kernel32.PageExecuteReadWrite});
+		IF adr = NilVal THEN (* allocation failed *)
+			adr := Kernel32.VirtualAlloc(NilVal, memBlkSize, {Kernel32.MEMCommit}, {Kernel32.PageExecuteReadWrite});
+		END;
+		*)
+		Trace.String("first heap block intVal "); Trace.Int(initVal,1); Trace.Ln;
+		Trace.String("first heap block memBlkSize "); Trace.Int(memBlkSize,1); Trace.Ln;
+		Trace.String("first heap block adr "); Trace.Int(adr,1); Trace.Ln;
+		ASSERT(adr # 0);
+
+		alignOffset := (-adr) MOD StaticBlockSize;
+
+		memHeaderAdr := adr + alignOffset;  	(* force alignment of memory block start *)
+		memBlockAdr := memHeaderAdr + MemBlockHeaderSize;
+		memBlock := S.VAL(MemoryBlock, memBlockAdr);
+		beginBlockAdr := memHeaderAdr + memDescSize;
+
+		memBlock.next := NIL;
+		memBlock.startAdr := adr;
+		memBlock.size := memBlkSize;
+
+		beginBlockAdr := memHeaderAdr + memDescSize;
+		endBlockAdr := adr + memBlkSize - alignOffset;
+		memBlock.beginBlockAdr := beginBlockAdr;
+		memBlock.endBlockAdr := endBlockAdr;
+
+		(* correct fields *)
+		S.PUT(memBlockAdr + HeapBlockOffset, memHeaderAdr + BlockHeaderSize);	(* set reference to header part of memory block correctly *)
+		S.PUT(memBlockAdr + TypeDescOffset, 0);										(* set type descriptor field of memory block to default value, memory blocks are not traced by GC *)
+		S.PUT(memHeaderAdr + BlockHeaderSize + DataAdrOffset, memBlockAdr);		(* set dataAdr of RecordBlockDesc to correct value *)
+		S.PUT(memHeaderAdr + BlockHeaderSize + 2*AddressSize , memBlkSize);
+
+		(* fill first heap block *)
+		S.PUT(beginBlockAdr,0);
+		S.PUT(beginBlockAdr+AddressSize,0);
+		S.PUT(beginBlockAdr+2*AddressSize,0);
+		S.PUT(beginBlockAdr+3*AddressSize,beginBlockAdr+7*AddressSize);
+		S.PUT(beginBlockAdr+4*AddressSize,endBlockAdr-beginBlockAdr);
+		S.PUT(beginBlockAdr+5*AddressSize,beginBlockAdr+2*AddressSize);
+		S.PUT(beginBlockAdr+6*AddressSize,0);
+
+		memoryBlock := memBlock;
+		
+		TRACE("InitHeap done");
+	END InitHeap;
+
+	(*
+	PROCEDURE InitHeap;
+	VAR heapAdr, firstBlock: ADDRESS; size: SIZE;  
+	BEGIN
+		Unix.Dlsym( 0, "heapAdr", ADDRESSOF( heapAdr ) );  
+		Unix.Dlsym( 0, "heapSize", ADDRESSOF( size ) );  
+		firstBlock := heapAdr + ((-heapAdr - AddrSize) MOD BlockSize);
+		size := heapAdr + size - BlockSize - firstBlock;  DEC( size, size MOD BlockSize + BlockSize );
+
+		firstMemBlock.next := NIL;
+		firstMemBlock.startAdr := heapAdr;
+		firstMemBlock.beginBlockAdr :=  firstBlock;
+		firstMemBlock.endBlockAdr := firstBlock + size;  
+		firstMemBlock.size := size;
+	
+		memBlockHead := S.VAL( MemoryBlock, ADDRESSOF( firstMemBlock ) );
+		memBlockTail := memBlockHead;
+	END InitHeap;
+	*)
+
+	PROCEDURE InitConfig;
+	VAR a: ADDRESS;  i: LONGINT;  c: CHAR;
+	BEGIN
+		a := Unix.getenv( ADDRESSOF( "AOSCONFIG" ) );
+		IF a = 0 THEN  config := DefaultConfig
+		ELSE
+			REPEAT
+				S.GET( a, c );  INC( a );  config[i] := c;  INC( i )
+			UNTIL c = 0X
+		END
+	END InitConfig;
+	
+	
+	PROCEDURE UpdateTicks*;
+	BEGIN
+		ticks := SHORT( (GetTimer() - timer0) DIV (mhz * 1000) );
+	END UpdateTicks;
+	
+	
+	PROCEDURE InitThreads;
+	VAR res: BOOLEAN; 
+	BEGIN
+		res := Unix.ThrInitialize( prioLow, prioHigh );
+		IF ~res THEN
+			Trace.StringLn( "Machine.InitThreads: no threads support in boot environment.  teminating" ); 
+			Unix.exit( 1 )
+		END;
+		IF Glue.debug # {} THEN
+			Trace.String( "Threads initialized, priorities low, high: " ); 
+			Trace.Int( prioLow, 0 ); Trace.String( ", " ); Trace.Int( prioHigh, 0 );
+			Trace.Ln
+		END
+	END InitThreads;
+	
+	PROCEDURE CPUSpeed;
+	VAR t0, t1: HUGEINT; 
+	BEGIN
+		t0 := GetTimer();  Unix.ThrSleep( 100 );  t1 := GetTimer();
+		mhz := (t1 - t0) DIV 100000;
+		IF Glue.debug # {} THEN
+			Trace.String( "CPU speed: ~" );  Trace.Int( SHORT( mhz ), 0);  Trace.String( " MHz" );  Trace.Ln
+		END
+	END CPUSpeed;
+	
+	PROCEDURE Log( c: CHAR );
+	VAR ignore: LONGINT;
+	BEGIN
+		ignore := Unix.write( 1, ADDRESSOF( c ), 1 );
+		ignore := Unix.write( logfile, ADDRESSOF( c ), 1 );
+	END Log;
+	
+	PROCEDURE LogFileOnly( c: CHAR );
+	VAR ignore: LONGINT;
+	BEGIN
+		ignore := Unix.write( logfile, ADDRESSOF( c ), 1 );
+	END LogFileOnly;
+	
+	
+	PROCEDURE InitLog;
+	VAR name, cmd: ARRAY 32 OF CHAR;  pid, i: LONGINT; 
+	BEGIN
+		name := "AOS.xxxxx.Log";
+		pid := Unix.getpid();  i := 8;
+		REPEAT
+			name[i] := CHR( pid MOD 10 + ORD( '0' ) );  DEC( i );
+			pid := pid DIV 10;		
+		UNTIL i = 3;
+		logfile := Unix.open( ADDRESSOF( name ), Unix.rdwr + Unix.creat + Unix.trunc, Unix.rwrwr );
+		IF Unix.argc > 2 THEN
+			Unix.GetArgval( "-x", cmd );
+			IF cmd # "" THEN  SilentLog;  standaloneAppl := TRUE 
+			ELSE  VerboseLog 
+			END
+		ELSE
+			VerboseLog
+		END
+	END InitLog;
+	
+	PROCEDURE SilentLog*;
+	BEGIN
+		Trace.Char := LogFileOnly
+	END SilentLog;
+	
+	PROCEDURE VerboseLog*;
+	BEGIN
+		Trace.Char := Log
+	END VerboseLog;
+
+	
+	PROCEDURE Append( VAR a: ARRAY OF CHAR; CONST this: ARRAY OF CHAR );
+	VAR i, j: LONGINT;
+	BEGIN
+		i := 0;  j := 0;  
+		WHILE a[i] # 0X DO  INC( i )  END;
+		WHILE (i < LEN( a ) - 1) & (this[j] # 0X) DO a[i] := this[j];  INC( i );  INC( j )  END;
+		a[i] := 0X
+	END Append;
+	
+
+	PROCEDURE Init;
+	VAR vendor: Vendor; ver: LONGINT;
+	BEGIN
+		TRACE("Machine Init Start");
+
+		(*
+		Unix.Dlsym( 0, "Unix.ThrInitialize",	ADDRESSOF( Unix.ThrInitialize ) );
+		
+		Unix.Dlsym( 0, "Unix.MtxInit",		ADDRESSOF( Unix.MtxInit ) );
+		Unix.Dlsym( 0, "Unix.MtxDestroy",	ADDRESSOF( Unix.MtxDestroy ) );
+		Unix.Dlsym( 0, "Unix.MtxLock",		ADDRESSOF( Unix.MtxLock ) );
+		Unix.Dlsym( 0, "Unix.MtxUnlock",	ADDRESSOF( Unix.MtxUnlock ) );
+		
+		Unix.Dlsym( 0, "conInit",		ADDRESSOF( conInit ) );
+		Unix.Dlsym( 0, "conDestroy",	ADDRESSOF( conDestroy ) );
+		Unix.Dlsym( 0, "conWait",		ADDRESSOF( conWait ) );
+		Unix.Dlsym( 0, "conSignal",	ADDRESSOF( conSignal ) );
+			
+		Unix.Dlsym( 0, "thrSleep",	ADDRESSOF( thrSleep ) );
+		Unix.Dlsym( 0, "thrThis",		ADDRESSOF( thrThis ) );
+		*) 
+		standaloneAppl := FALSE;
+		
+		COPY( Unix.Version, version );  Append( version, Version );
+		timer0 := GetTimer( );  ticks := 0;
+		InitThreads;
+		TRACE(0);
+		InitLocks;
+		TRACE(1);
+		traceHeap := 1 IN Glue.debug;
+		InitConfig;
+		TRACE(2);
+		InitLog;
+		TRACE(3);
+		CPUSpeed;
+		TRACE(4);
+		IF CpuIdSupported() THEN
+			CPUID( vendor, ver, features, features2 );	 SetupSSE2Ext
+		END;
+		TRACE(5);
+		fcr := (FCR() - {0,2,3,10,11}) + {0..5,8,9};	(* default FCR RC=00B *)
+		TRACE("Machine Init End");
+	END Init;
+
+	PROCEDURE {INITIAL} Init0;
+	BEGIN
+		Init;
+	END Init0;
+	
+END Machine.
+
+(*
+03.03.1998	pjm	First version
+30.06.1999	pjm	ProcessorID moved to AosProcessor
+*)
+
+(**
+Notes
+
+This module defines an interface to the boot environment of the system. The facilities provided here are only intended for the lowest levels of the system, and should never be directly imported by user modules (exceptions are noted below). They are highly specific to the system hardware and firmware architecture.
+
+Typically a machine has some type of firmware that performs initial testing and setup of the system. The firmware initiates the operating system bootstrap loader, which loads the boot file. This module is the first module in the statically linked boot file that gets control.
+
+There are two more-or-less general procedures in this module: GetConfig and StrToInt. GetConfig is used to query low-level system settings, e.g., the location of the boot file system. StrToInt is a utility procedure that parses numeric strings.
+
+Config strings:
+
+ExtMemSize	Specifies size of extended memory (above 1MB) in MB. This value is not checked for validity. Setting it false may cause the system to fail, possible after running for some time. The memory size is usually detected automatically, but if the detection does not work for some reason, or if you want to limit the amount of memory detected, this string can be set. For example, if the machine has 64MB of memory, this value can be set as ExtMemSize="63".
+*)
+

+ 799 - 0
source/Generic.Unix.Objects.Mod

@@ -0,0 +1,799 @@
+(* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
+
+MODULE Objects;   (** AUTHOR "pjm, G.F."; PURPOSE "Active object runtime support"; *)
+
+
+IMPORT S := SYSTEM, Trace, Glue, Unix, Machine, Heaps, Modules;
+
+CONST
+
+	(*! Process flags, meaningless in Unix ports !!! *)
+	PleaseHalt* = 10;		(* Process requested to Halt itself soon *)
+	Unbreakable*= 11;		(* FINALLY shall not catch HALT exception (PleaseHalt is also set) *)
+	SelfTermination*=12;	(* Indicates the process has requested to terminate ifself (PleaseHalt is also set) *)
+	Preempted* = 27;		(* Has been preempted. *)
+	Resistant* = 28;		(* Can only be destroyed by itself *)
+
+
+	MinPriority*	= Unix.ThreadLow;
+	Low*			= Unix.ThreadLow + 1;
+	Normal*		= Unix.ThreadNormal;
+	High*			= Unix.ThreadHigh - 2;
+	GCPriority*	= Unix.ThreadHigh - 1;
+	Realtime*	= Unix.ThreadHigh;
+
+	(* Process flag defined by compiler in OPC.CallRecBody *)
+	Restart* = 0;	(* Restart/Destroy process on exception *)
+
+	(* Process modes (in UnixAos Running means Running or Ready!) *)
+	Unknown* = 0;  Ready* = 1;  Running* = 2;  AwaitingLock* = 3;
+	AwaitingCond* = 4;  AwaitingEvent* = 5;  Terminated* = 6;
+
+	Second* = 1000;	(* frequency of ticks increments in Hz *)
+
+	DefaultStacksize = 128*1024;
+	
+	AddrSize = SIZEOF( ADDRESS )
+
+
+VAR
+	(* timer *)
+	timerActivity		: TimerActivity;
+	timers				: Timer;  
+	timerListMutex	: Unix.Mutex_t;
+	
+	(* processes *)
+	mainProcess	: Process;	(* runs the GC *)
+	root-	: Process;	(*!  Anchor of all instantiated threads in system *)
+	stacksize: LONGINT;		(* stack size of active objects, adjustable via boot parameter *)
+	
+	processList		: Unix.Mutex_t;
+	createProcess	: Unix.Mutex_t;
+	startProcess	: Unix.Mutex_t;
+	childrunning		: Unix.Condition_t;
+		
+	newProcess: Process;
+	nextPID: LONGINT;
+	
+	(* garbage colletion *)
+	gcFinished: Unix.Condition_t; igc: Unix.Mutex_t;
+	collect: BOOLEAN;
+	finalizerCaller	: FinalizerCaller;
+	finCaller			: Process;
+	
+	
+	(* the dummy parameters assure proper stack alignment when compiled with 
+		option "\A" or "--darwinHost" *)
+	(*
+	thrStart: 			PROCEDURE {REALTIME, C} ( p: PROCEDURE;  stackLen: LONGINT ): Unix.Thread_t;
+	Unix.ThrThis: 			PROCEDURE {REALTIME, C} ( dummy: LONGINT ): Unix.Thread_t;
+	Unix.ThrSleep: 			PROCEDURE {REALTIME, C} ( ms: LONGINT );
+	Unix.ThrYield: 			PROCEDURE {REALTIME, C} ( dummy: LONGINT );
+	Unix.ThrExit: 			PROCEDURE {REALTIME, C} ( dummy: LONGINT );
+	Unix.ThrSuspend: 		PROCEDURE {REALTIME, C} ( t: Unix.Thread_t );
+	Unix.ThrResume: 		PROCEDURE {REALTIME, C} ( t: Unix.Thread_t );
+	Unix.ThrSetPriority: 	PROCEDURE {REALTIME, C} ( t: Unix.Thread_t;  prio: LONGINT );
+	Unix.ThrGetPriority: 	PROCEDURE {REALTIME, C} ( t: Unix.Thread_t ): LONGINT;
+	thrKill: 			PROCEDURE {REALTIME, C} ( t: Unix.Thread_t );
+	*)
+	
+
+TYPE
+
+	CpuCyclesArray* = ARRAY Machine.MaxCPU OF HUGEINT;
+
+	ProtectedObject = POINTER TO RECORD END;
+
+	ObjectHeader = Heaps.ProtRecBlock;
+
+	ProcessQueue = Heaps.ProcessQueue;
+
+	EventHandler* = PROCEDURE  {DELEGATE};
+
+
+
+	Timer* =  OBJECT
+	VAR
+		next: Timer;
+		trigger: LONGINT;
+		handler: EventHandler
+	END Timer;
+			
+	TimerActivity = OBJECT		
+	VAR 
+		t, r: Timer;  h: EventHandler;  restart: BOOLEAN;
+		
+		PROCEDURE UpdateTicks;
+		BEGIN {EXCLUSIVE}
+			Machine.UpdateTicks
+		END UpdateTicks;
+		
+		PROCEDURE Restart;
+		BEGIN {EXCLUSIVE}
+			restart := TRUE
+		END Restart;
+		
+	BEGIN {ACTIVE, SAFE, PRIORITY(High)}
+		restart := FALSE;
+		LOOP
+			t := timers;
+			IF t # NIL THEN				
+				h := NIL;  r := NIL;
+				BEGIN {EXCLUSIVE}
+					AWAIT( (Machine.ticks >= t.trigger) OR restart );  restart := FALSE;
+					IF Machine.ticks >= t.trigger THEN
+						h := t.handler;  r := t
+					END
+				END;
+				IF r # NIL THEN  Remove( r )  END;
+				IF h # NIL THEN  (* not canceled *) h  END
+			ELSE				
+				BEGIN{EXCLUSIVE}
+					AWAIT( restart );  restart := FALSE;
+				END
+			END
+		END
+	END TimerActivity;
+
+
+
+	FinalizedCollection* = OBJECT (* base type for collection, extended in Kernel.Mod *)
+		PROCEDURE RemoveAll*(obj: ANY); (** abstract *)
+		BEGIN HALT(301) END RemoveAll;
+	END FinalizedCollection;
+
+	FinalizerNode* = POINTER TO RECORD (Heaps.FinalizerNode)
+		c*: FinalizedCollection (* base type for collection containing object *)
+	END;
+
+		
+	FinalizerCaller = OBJECT	(* separate active object that calls finalizers *)
+	VAR 
+		n: Heaps.FinalizerNode;  start: BOOLEAN;
+	
+		PROCEDURE Start;
+		BEGIN {EXCLUSIVE}
+			start := TRUE
+		END Start;
+		
+	BEGIN {ACTIVE, SAFE, PRIORITY(High)}
+		finCaller := CurrentProcess( );  start := FALSE;
+		LOOP 
+			BEGIN {EXCLUSIVE} AWAIT( start ) END;
+			start := FALSE;
+			LOOP
+				n := Heaps.GetFinalizer();
+				IF n = NIL THEN EXIT END;
+				IF n IS FinalizerNode THEN
+					n(FinalizerNode).c.RemoveAll(n.objStrong)	(* remove it if it is not removed yet *)
+				END;
+				IF n.finalizer # NIL THEN
+					n.finalizer(n.objStrong)	(* may acquire locks *)
+				END;
+			END;
+			Machine.ReleaseGC
+		END
+	END FinalizerCaller;
+	
+
+
+	Body = PROCEDURE ( self: ProtectedObject );
+	Condition = PROCEDURE ( slink: ADDRESS ): BOOLEAN;
+	
+	Process* = OBJECT (Heaps.ProcessLink)
+	VAR
+		threadId			: Unix.Thread_t;
+		nextProcess-	: Process;	(* next in list of all processes *)
+		stackBottom	-	: ADDRESS;
+		SP-				: ADDRESS;	(* SP value at last NEW *)
+		id-				: LONGINT;
+		body			: Body;
+		mode-		: LONGINT;
+		flags-			: SET;
+		priority-		: LONGINT;	(* only effective if Aos is running SUID root *)
+		succ			: Process;   		  	(* in ProcessQueue *)
+		obj-			: ProtectedObject;	(* associated active object *)
+		condition-	: Condition;   			(* awaited process' condition *)
+		condFP-		: ADDRESS;			(* awaited process' condition's context *)
+		continue		: Unix.Condition_t;	(* gets signaled when condition yields true *)
+		waitingOn-	: ProtectedObject;
+		procID-		: LONGINT;				(* processor ID where running, not used in UnixAos *)
+		state-			: Machine.State;		(*! not used in UnixAos! *)
+		state0	: ARRAY 2048 OF CHAR;		(* thread state at body start, used for restart after trap *)
+					
+				
+		PROCEDURE FindRoots*;
+		VAR sp, ptr: ADDRESS;
+		BEGIN
+			IF mode # Terminated THEN
+				sp := SP;
+				WHILE sp < stackBottom DO  
+					S.GET( sp, ptr );  
+					IF (ptr # 0) & (ptr MOD 8 = 0) THEN  Heaps.Candidate( ptr )  END;  
+					INC( sp, AddrSize )  
+				END;
+			END;
+			Heaps.Mark( nextProcess ) 
+		END FindRoots;
+				
+		PROCEDURE Cancel;
+		VAR pt, t: Process;  kt: Unix.Thread_t;
+		BEGIN
+			IF SELF = CurrentProcess() THEN  Exit
+			ELSE
+				Machine.Acquire( Machine.X11 );  (* let the thread to be killed first finish its last I/O, if any *)
+				Unix.MtxLock( processList );
+					pt := NIL; t := root;  kt := 0;
+					WHILE (t # NIL ) & (t # SELF) DO  pt := t;  t := t.nextProcess  END;
+					IF t = SELF THEN
+						kt := threadId;
+						IF pt = NIL THEN  root := t.nextProcess  ELSE  pt.nextProcess := t.nextProcess  END;
+					END;
+				Unix.MtxUnlock( processList );
+				IF kt # 0 THEN  Unix.ThrKill( kt )  END;
+				Machine.Release( Machine.X11 );
+			END
+		END Cancel;
+
+		PROCEDURE GetPriority( ): LONGINT;
+		BEGIN
+			RETURN Unix.ThrGetPriority( threadId ) 
+		END GetPriority;
+
+		PROCEDURE SetPriority( prio: LONGINT );
+		VAR pr: LONGINT;
+		BEGIN
+			pr := max( Machine.prioLow, min( prio, Machine.prioHigh ) );
+			Unix.ThrSetPriority( threadId, pr );	(* works only if SUID root *)
+			priority := GetPriority( )	
+		END SetPriority;
+				
+				
+		PROCEDURE & Initialize( obj: ProtectedObject;  bodyProc: Body;  prio: LONGINT; fl: SET; stacksize: LONGINT);
+		VAR  thr: Unix.Thread_t;
+		BEGIN
+			TRACE(root);
+			SELF.obj := obj;  condition := NIL;  continue := Unix.ConInit(0);
+			flags := fl;
+			priority := prio;
+			nextProcess := NIL;
+			IF root # NIL THEN
+				newProcess := SELF;
+				ASSERT( bodyProc # NIL );
+				body := bodyProc;  
+				Unix.MtxLock( startProcess );
+				TRACE(body);
+					thr := Unix.ThrStart( BodyStarter, stacksize );
+				TRACE(body);
+					Unix.ConWait( childrunning, startProcess );
+				Unix.MtxUnlock( startProcess );
+				RegisterFinalizer( SELF, FinalizeProcess );
+			ELSE 
+				(* first process *)
+				stackBottom := Glue.stackBottom;  
+				SP := Machine.CurrentSP( );
+				TRACE(root);
+				threadId := Unix.ThrThis(0);
+				id := 0;  nextPID := 1;
+				root := SELF;
+				mainProcess := SELF;
+				mode := Running;
+			END;
+		END Initialize;
+				
+	END Process;
+
+
+	
+	PROCEDURE BodyStarter;
+	VAR p{UNTRACED}: Process;  res: LONGINT; prevBP: ADDRESS;
+	BEGIN
+		Unix.MtxLock( startProcess );
+			p := newProcess;  newProcess := NIL;
+			p.threadId := Unix.ThrThis(0);  
+			p.id := nextPID;  INC( nextPID );
+			p.SP := Machine.CurrentSP(  );  
+			p.stackBottom := Machine.CurrentBP( );
+			S.GET( p.stackBottom, prevBP );
+			S.PUT( prevBP, S.VAL( ADDRESS, 0 ) );	(* for terminating Reflection.StackTraceBack *)
+			Unix.MtxLock( processList );
+				p.nextProcess := root;  root := p;
+			Unix.MtxUnlock( processList );
+			Unix.ConSignal( childrunning );
+		Unix.MtxUnlock( startProcess );
+
+		p.SetPriority( p.priority );
+		IF Restart IN p.flags THEN
+			res := Unix.sigsetjmp( ADDRESSOF( p.state0[0] ), 1 );
+		END;
+		p.mode := Running;
+		p.body( p.obj );
+		p.mode := Terminated;
+		Exit
+	END BodyStarter;
+
+
+
+
+	(*---------------------   create,  lock,  await,  unlock   -------------------------*)
+	
+	PROCEDURE InitProtHeader( hdr: ObjectHeader );
+	BEGIN
+		hdr.mtx := Unix.MtxInit( 0 );  hdr.enter := Unix.ConInit( 0 );  hdr.lockedBy := NIL;  
+	END InitProtHeader;
+	
+	
+	PROCEDURE CreateProcess*( body: Body;  priority: LONGINT;  flags: SET;  obj: ProtectedObject );
+	VAR p: Process;  hdr: ObjectHeader;
+	BEGIN
+		TRACE(body);
+		Unix.MtxLock( createProcess );
+		S.GET( S.VAL( ADDRESS, obj ) + Heaps.HeapBlockOffset, hdr );  InitProtHeader( hdr );
+		IF priority = 0 THEN  priority := Normal  END;
+		NEW( p, obj, body, priority, flags, stacksize ) ;	(* execute BodyStarter as new (posix or solaris) thread *)
+		Unix.MtxUnlock( createProcess );
+		RegisterFinalizer( obj, FinalizeActiveObj )
+	END CreateProcess;
+
+	PROCEDURE Lock*( obj: ProtectedObject;  exclusive: BOOLEAN );
+	VAR hdr: ObjectHeader;  p: Process; 
+	BEGIN
+		ASSERT( exclusive );   (* shared not implemented yet *)
+		S.GET( S.VAL( ADDRESS, obj ) + Heaps.HeapBlockOffset, hdr );
+		p := CurrentProcess();
+		p.mode := AwaitingLock;
+		IF hdr.mtx = 0 THEN  InitProtHeader( hdr )  END;
+		Unix.MtxLock( hdr.mtx );
+		WHILE hdr.lockedBy # NIL DO
+			(* wait until threads with complied AWAIT conditions have left the monitor *)
+			Unix.ConWait( hdr.enter, hdr.mtx );
+		END;
+		p.mode := Running;  hdr.lockedBy := p;  p.waitingOn := NIL
+	END Lock;
+
+	PROCEDURE Await*( cond: Condition;  slink: ADDRESS;  obj: ProtectedObject;  flags: SET );
+	VAR hdr: ObjectHeader;  p, c: Process;
+	BEGIN
+		IF 1 IN flags THEN  (* compiler did not generate IF *)
+			IF cond( slink ) THEN  (* condition already true *)  RETURN  END
+		END;
+		S.GET( S.VAL( ADDRESS, obj ) + Heaps.HeapBlockOffset, hdr );  c := NIL;
+		IF hdr.awaitingCond.head # NIL THEN  c := FindCondition( hdr.awaitingCond )  END;
+		
+		p := CurrentProcess();  p.succ := NIL;  p.condition := cond;  p.condFP := slink;   
+		p.waitingOn := obj;  p.mode := AwaitingCond;
+		
+		Put( hdr.awaitingCond, p );
+		
+		hdr.lockedBy := c;
+		IF c # NIL THEN  Unix.ConSignal( c.continue )  ELSE  Unix.ConSignal( hdr.enter )  END;
+		Unix.ConWait( p.continue, hdr.mtx );   
+		
+		p.mode := Running;  hdr.lockedBy := p;  p.waitingOn := NIL
+	END Await;
+
+	PROCEDURE Unlock*( obj: ProtectedObject;  dummy: BOOLEAN );
+	VAR hdr: ObjectHeader;  c: Process;
+	BEGIN
+		S.GET( S.VAL( ADDRESS, obj ) + Heaps.HeapBlockOffset, hdr );  c := NIL;
+		IF hdr.awaitingCond.head # NIL THEN  c := FindCondition( hdr.awaitingCond )  END;
+		
+		hdr.lockedBy := c;
+		IF c # NIL THEN  Unix.ConSignal( c.continue )  ELSE  Unix.ConSignal( hdr.enter )  END;
+		Unix.MtxUnlock( hdr.mtx );
+	END Unlock;
+	
+	
+	
+	PROCEDURE FindCondition( VAR q: ProcessQueue ): Process;
+	VAR first, cand: Process;
+	BEGIN
+		Get( q, first );
+		IF first.condition( first.condFP ) THEN  RETURN first  ELSE  Put( q, first )  END;
+		WHILE q.head # first DO
+			Get( q, cand );
+			IF cand.condition( cand.condFP ) THEN  RETURN cand  ELSE  Put( q, cand )  END;
+		END;
+		RETURN NIL
+	END FindCondition;
+
+	PROCEDURE Get( VAR queue: ProcessQueue;  VAR new: Process );
+	VAR t: Process;
+	BEGIN
+		t := queue.head(Process);
+		IF t # NIL THEN
+			IF t = queue.tail THEN  queue.head := NIL;  queue.tail := NIL
+			ELSE  queue.head := t.succ;  t.succ := NIL
+			END
+		END;
+		new := t
+	END Get;
+
+	PROCEDURE Put( VAR queue: ProcessQueue;  t: Process );
+	BEGIN
+		IF queue.head = NIL THEN  queue.head := t  ELSE  queue.tail(Process).succ := t  END;
+		queue.tail := t
+	END Put;
+	
+	
+	
+	(*-------------------------------------------------------------------------*)
+	
+	PROCEDURE Terminate*;
+	BEGIN
+		Exit
+	END Terminate;
+
+	PROCEDURE TerminateThis*( p: Process; unbreakable: BOOLEAN );
+	BEGIN
+		p.mode := Terminated;
+		p.Cancel
+	END TerminateThis;
+	
+	PROCEDURE SetPriority*( pri: LONGINT );		(* Set the current process' priority. *)
+	VAR me: Process;
+	BEGIN
+		me := CurrentProcess();
+		me.SetPriority( pri )
+	END SetPriority;
+
+	PROCEDURE Sleep*( ms: LONGINT );
+	BEGIN
+		Unix.ThrSleep( ms )
+	END Sleep;
+
+	PROCEDURE Yield*;	(* Relinquish control. *)
+	BEGIN
+		Unix.ThrYield(0);
+	END Yield;
+	
+	(* Return current process. (DEPRECATED, use ActiveObject) *)
+	PROCEDURE CurrentProcess*( ): Process;	
+	VAR me: Unix.Thread_t;  p: Process;
+	BEGIN
+		me := Unix.ThrThis(0);
+		Unix.MtxLock( processList );
+		p := root;
+		WHILE (p # NIL) & (p.threadId # me) DO  p := p.nextProcess  END;
+		Unix.MtxUnlock( processList );
+		RETURN p
+	END CurrentProcess;
+
+	
+	(* Return the active object currently executing. *)
+	PROCEDURE ActiveObject*( ): ANY;		
+	VAR p: Process;
+	BEGIN
+		p := CurrentProcess();
+		RETURN p.obj 
+	END ActiveObject;
+	
+	
+	(* Return stack bottom of process. For compatibility WinAos/UnixAos/NativeAos  *)
+	PROCEDURE GetStackBottom*(p: Process): ADDRESS;
+	BEGIN
+		RETURN p.stackBottom
+	END GetStackBottom;
+
+
+	PROCEDURE GetProcessID*( ): LONGINT;
+	VAR p: Process;
+	BEGIN
+		p := CurrentProcess();
+		RETURN p.id;
+	END GetProcessID;
+
+	
+	PROCEDURE GetCpuCycles*( process : Process; VAR cpuCycles: CpuCyclesArray; all: BOOLEAN );
+	VAR i: LONGINT;
+	BEGIN
+		ASSERT( process # NIL );
+		FOR i := 0 TO Machine.MaxCPU-1 DO  cpuCycles[i] := 0  END;
+	END GetCpuCycles;
+	
+	
+	
+	(*-----------------------------------------------------------------------*)
+	
+	
+	PROCEDURE min( a, b: LONGINT ): LONGINT;
+	BEGIN
+		IF a <= b THEN  RETURN a  ELSE  RETURN b  END
+	END min;
+
+	PROCEDURE max( a, b: LONGINT ): LONGINT;
+	BEGIN
+		IF a >= b THEN  RETURN a  ELSE  RETURN b  END
+	END max;
+	
+	
+	PROCEDURE RegisterFinalizer( obj: ANY;  fin: Heaps.Finalizer );
+	VAR n: Heaps.FinalizerNode;
+	BEGIN
+		NEW( n ); n.finalizer := fin;  Heaps.AddFinalizer( obj, n );
+	END RegisterFinalizer;
+
+
+	PROCEDURE FinalizeActiveObj( obj: ANY );
+	VAR p: Process;
+	BEGIN
+		Unix.MtxLock( processList );
+			p := root;
+			WHILE (p # NIL) & (p.obj # obj) DO p := p.nextProcess  END;
+		Unix.MtxUnlock( processList );
+		IF (p # NIL) & (p.obj = obj) THEN
+			p.mode := Terminated;
+			Unix.ConDestroy( p.continue );  p.continue := 0;
+			FinalizeProtObject( obj );
+			p.Cancel
+		END;
+	END FinalizeActiveObj;
+
+	PROCEDURE FinalizeProtObject( obj: ANY );
+	VAR hdr: ObjectHeader;
+	BEGIN
+		S.GET( S.VAL( ADDRESS, obj ) + Heaps.HeapBlockOffset, hdr );
+		IF hdr.mtx # 0 THEN
+			Unix.MtxDestroy( hdr.mtx );  hdr.mtx := 0
+		END
+	END FinalizeProtObject;
+
+
+	PROCEDURE FinalizeProcess( obj: ANY );
+	VAR p: Process;
+	BEGIN
+		p := obj(Process);
+		IF p.continue # 0 THEN
+			Unix.ConDestroy( p.continue );  p.continue := 0
+		END
+	END FinalizeProcess;
+	
+	(* Terminate calling thread. *)
+	PROCEDURE Exit;
+	VAR prev, p, me: Process;
+	BEGIN
+		me := CurrentProcess();
+		me.mode := Terminated;
+		Unix.MtxLock( processList );
+			prev := NIL;  p := root;
+			WHILE (p # NIL ) & (p # me) DO  prev := p;  p := p.nextProcess  END;
+			IF p = me THEN
+				IF prev = NIL THEN  root := p.nextProcess  ELSE  prev.nextProcess := p.nextProcess  END;
+			END;
+		Unix.MtxUnlock( processList );
+		Unix.ThrExit(0)
+	END Exit;
+
+	PROCEDURE ExitTrap*;
+	VAR p: Process;
+	BEGIN
+		p := CurrentProcess();
+		(* restart the object body if it was given the SAFE flag *)
+		IF Restart IN p.flags THEN
+			Unix.siglongjmp( ADDRESSOF( p.state0[0] ), 1 )
+		END;
+		Exit
+	END ExitTrap;
+
+
+
+
+	(*---------------------------- Timer --------------------------------*)
+
+
+	PROCEDURE Remove( t: Timer );  (* remove timer from list of active timers *)
+	VAR p, x: Timer;
+	BEGIN
+		Unix.MtxLock( timerListMutex ); 
+		t.trigger := 0;  t.handler := NIL;
+		IF timers # NIL THEN
+			IF t = timers THEN  
+				timers := t.next
+			ELSE
+				p := timers;  x := p.next;
+				WHILE (x # NIL) & (x # t)  DO  p := x;  x := p.next  END;
+				IF x = t THEN  p.next := t.next  END
+			END;
+			t.next := NIL
+		END;
+		Unix.MtxUnlock( timerListMutex )
+	END Remove;
+	
+	PROCEDURE Insert( t: Timer );
+	VAR  p, x: Timer;
+	BEGIN
+		Unix.MtxLock( timerListMutex ); 
+		p := NIL;  x := timers;
+		WHILE (x # NIL) & (x.trigger < t.trigger)  DO  p := x;  x := p.next  END;
+		t.next := x;
+		IF p = NIL THEN  timers := t  ELSE   p.next := t  END;
+		Unix.MtxUnlock( timerListMutex )
+	END Insert;
+
+	PROCEDURE SetTimeout*( t: Timer;  h: EventHandler;  ms: LONGINT );
+	BEGIN
+		ASSERT( ( t # NIL) & ( h # NIL) );
+		Remove( t );  
+		IF ms < 1 THEN ms := 1 END;
+		t.trigger := Machine.ticks + ms;  t.handler := h;
+		Insert( t );
+		timerActivity.Restart
+	END SetTimeout;
+
+	PROCEDURE SetTimeoutAt*( t: Timer;  h: EventHandler;  ms: LONGINT );
+	BEGIN
+		ASSERT( (t # NIL) & (h # NIL) );
+		Remove( t );
+		t.trigger := ms;  t.handler := h;
+		Insert( t );
+		timerActivity.Restart
+	END SetTimeoutAt;
+
+	PROCEDURE CancelTimeout*( t: Timer );
+	BEGIN
+		Remove( t )
+	END CancelTimeout;
+
+
+
+	(*--------------------  Garbage Collection  ------------------------------------*)
+	
+	PROCEDURE SuspendActivities;
+	VAR t: Process;  
+	BEGIN
+		t := root;
+		WHILE t # NIL DO
+			IF (t # mainProcess) & (t # finCaller) THEN  Unix.ThrSuspend( t.threadId )  END;
+			t := t.nextProcess
+		END;
+	END SuspendActivities;
+
+	PROCEDURE ResumeActivities;
+	VAR t: Process;  
+	BEGIN
+		t := root;
+		WHILE t # NIL DO
+			IF (t # mainProcess) & (t # finCaller) THEN  Unix.ThrResume( t.threadId )  END;
+			t := t.nextProcess
+		END;
+	END ResumeActivities;
+	
+
+	PROCEDURE SaveSP;   (* save current SP for usage by the GC *)
+	VAR me: Unix.Thread_t;  t: Process;
+	BEGIN
+		me := Unix.ThrThis(0);  t := root;
+		WHILE (t # NIL ) & (t.threadId # me) DO  t := t.nextProcess  END;
+		IF t # NIL THEN  t.SP := Machine.CurrentSP( )  END
+	END SaveSP;
+		
+	
+	PROCEDURE InvokeGC;
+	BEGIN
+		IF Machine.AcquireGC() THEN (* gets released by FinalizerCaller *)
+			collect := TRUE;
+			Unix.ConWait( gcFinished, igc )
+		END;
+	END InvokeGC;
+	
+	(*!	GCLoop gets called as last procedure in BootConsole (main thread). 
+		The stack of the main thread is not limited by the  boot parameter 'StackSize' !!
+	*)
+	PROCEDURE GCLoop*;	(* Timer and GC activity *)
+	BEGIN
+		SetPriority( GCPriority );
+		LOOP
+			IF collect THEN  
+				collect := FALSE;
+				Machine.Acquire( Machine.Heaps );
+				SuspendActivities;
+				Heaps.CollectGarbage( Modules.root );
+				Machine.Release( Machine.Heaps );
+				ResumeActivities;
+				finalizerCaller.Start;
+				Unix.ConSignal( gcFinished );
+			ELSE
+				Unix.ThrSleep( 10 ); 
+			END;
+			timerActivity.UpdateTicks
+		END
+	END GCLoop;	
+	
+	
+	
+	PROCEDURE CurrentProcessTime*(): HUGEINT;
+	BEGIN
+		RETURN  Machine.GetTimer()
+	END CurrentProcessTime;
+	
+	PROCEDURE TimerFrequency*(): HUGEINT;
+	BEGIN
+		RETURN Machine.mhz * 1000000
+	END TimerFrequency;
+
+	
+	(*----------------------------- initialization ----------------------------------*)
+	
+	PROCEDURE StartTimerActivity;
+	BEGIN
+		timerListMutex := Unix.MtxInit(0);  timers := NIL;  
+		NEW( timerActivity );
+	END StartTimerActivity;
+
+
+	PROCEDURE GetStacksize;
+	VAR str: ARRAY  32 OF  CHAR;  i: LONGINT;
+	BEGIN
+		Machine.GetConfig( "StackSize", str );
+		IF str = "" THEN  stacksize := DefaultStacksize
+		ELSE
+			i := 0;  stacksize := Machine.StrToInt( i, str );
+			stacksize := stacksize * 1024;
+		END;
+		IF Glue.debug # {} THEN
+			Trace.String( "Stacksize of active objects = " );
+			Trace.Int( stacksize DIV 1024, 0 );  Trace.StringLn( "K"  )
+		END;
+	END GetStacksize;
+
+	
+	PROCEDURE Convert;
+	VAR p: Process;
+	BEGIN
+		(* make current thread the first active object  *)
+		TRACE(p);
+		NEW( p, NIL, NIL, 0, {}, 0 );
+		TRACE(p);
+	END Convert;
+
+	PROCEDURE Init;
+	BEGIN
+		TRACE("Init");
+		(*
+		Unix.Dlsym( 0, "Unix.MtxInit",		ADDRESSOF( Unix.MtxInit ) );
+		Unix.Dlsym( 0, "Unix.MtxDestroy",	ADDRESSOF( Unix.MtxDestroy ) );
+		Unix.Dlsym( 0, "Unix.MtxLock",		ADDRESSOF( Unix.MtxLock ) );
+		Unix.Dlsym( 0, "Unix.MtxUnlock",	ADDRESSOF( Unix.MtxUnlock ) );
+		Unix.Dlsym( 0, "Unix.ConInit",		ADDRESSOF( Unix.ConInit ) );
+		Unix.Dlsym( 0, "Unix.ConDestroy",	ADDRESSOF( Unix.ConDestroy ) );
+		Unix.Dlsym( 0, "Unix.ConWait",		ADDRESSOF( Unix.ConWait ) );
+		Unix.Dlsym( 0, "Unix.ConSignal",	ADDRESSOF( Unix.ConSignal ) );
+		
+		Unix.Dlsym( 0, "thrStart",	ADDRESSOF( thrStart ) );
+		Unix.Dlsym( 0, "Unix.ThrThis",		ADDRESSOF( Unix.ThrThis ) );
+		Unix.Dlsym( 0, "Unix.ThrSleep",	ADDRESSOF( Unix.ThrSleep ) );
+		Unix.Dlsym( 0, "Unix.ThrYield",	ADDRESSOF( Unix.ThrYield ) );
+		Unix.Dlsym( 0, "Unix.ThrExit",		ADDRESSOF( Unix.ThrExit ) );
+		Unix.Dlsym( 0, "Unix.ThrSuspend",			ADDRESSOF( Unix.ThrSuspend ) );
+		Unix.Dlsym( 0, "Unix.ThrResume",			ADDRESSOF( Unix.ThrResume ) );
+		Unix.Dlsym( 0, "Unix.ThrGetPriority",	ADDRESSOF( Unix.ThrGetPriority ) );
+		Unix.Dlsym( 0, "Unix.ThrSetPriority",	ADDRESSOF( Unix.ThrSetPriority ) );
+		Unix.Dlsym( 0, "thrKill",				ADDRESSOF( thrKill ) );
+		*)
+		
+		createProcess := Unix.MtxInit( 0 );  processList := Unix.MtxInit( 0 );
+		startProcess := Unix.MtxInit(0);  childrunning := Unix.ConInit(0); 
+				
+		collect := FALSE;
+		igc := Unix.MtxInit( 0 );  gcFinished := Unix.ConInit( 0 ); 
+							
+		GetStacksize;  
+		TRACE(0);
+		Convert;
+		TRACE(1);
+		StartTimerActivity;
+		TRACE(2);
+		
+		NEW( finalizerCaller );
+		TRACE(3);
+		(*
+		Heaps.saveSP := SaveSP;
+		Heaps.GC := InvokeGC;
+		Heaps.InvokeGC := InvokeGC;
+		*)
+	END Init;
+
+BEGIN
+	Init;
+END Objects.
+