123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074 |
- MODULE Machine;
- (** AUTHOR "pjm,fof"; PURPOSE "Bootstrapping, configuration and machine interface, adaption to windows fof"; *)
- (* red marked parts are WinAos specific *)
- IMPORT SYSTEM, Trace, Kernel32;
- CONST
- Version = "WinAos Revision 5614 (04.07.2014)";
- DefaultConfigFile = "aos.ini";
- UserConfigFile = "myaos.ini";
- MaxCPU* = 8; (* dummy definition to make GC for both Win32 and I386 work *)
- DefaultObjectFileExtension* = ".Obw";
- (** bits in features variable *)
- MTTR* = 12; MMX* = 23;
- debug* = FALSE; (** display more debug output during booting *)
- CONST
- AddressSize = SIZEOF(ADDRESS);
- StaticBlockSize = 32; (* static heap block size *)
- BlockHeaderSize = 2 * AddressSize;
- RecordDescSize = 4 * AddressSize; (* needs to be adapted in case Heaps.RecordBlockDesc is changed *)
- (** standard lock levels (in order) *) (* also refer to Traps.Show *)
- TraceOutput* = 0; (* Trace output *)
- Memory* = 1; (* Virtual memory management, stack and page allocation *)
- Heaps* = 2; (* Storage allocation and Garbage collection *)
- Interrupts* = 3; (* Interrupt handling. *)
- Modules* = 4; (* Module list *)
- Objects* = 5; (* Ready queue *)
- Processors* = 6; (* Interprocessor interrupts *)
- KernelLog* = 7; (* Atomic output *)
- GC* = 8;
- MaxLocks = 9; (* { <= 32 } *)
- StrongChecks = FALSE;
- HeaderSize = 40H; (* cf. Linker0 *)
- EndBlockOfs = 38H; (* cf. Linker0 *)
- MemoryBlockOfs = BlockHeaderSize + RecordDescSize + BlockHeaderSize; (* memory block (including header) starts at offset HeaderSize *)
- MemBlockSize = 4*1024*1024; (* must be multiple of StaticBlockSize *)
- MinMemBlockSize = 4*1024*1024;
- NilVal = 0;
- Second* = 1000; (* frequency of ticks increments in Hz *)
- CONST
- (* error codes *)
- Ok* = 0;
- NilAdr* = -1; (* nil value for addresses (not same as pointer NIL value) *)
- IsCooperative* = FALSE;
- TYPE
- Vendor* = ARRAY 13 OF CHAR;
- IDMap* = ARRAY 16 OF SHORTINT;
- Range* = RECORD
- adr*, size*: LONGINT
- END;
- 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;
- (* dummy definition to make GC work for both I386 and Win32 - copied from I386.Machine.Mod, but not really used *)
- Stack* = RECORD (** values are read-only *)
- low: ADDRESS; (* lowest virtual address that may be allocated for stack *)
- adr*: ADDRESS; (* lowest address on allocated stack *) (* exported for Objects only *)
- high*: ADDRESS; (* next virtual address after stack *) (* exported for Objects only *)
- END;
- VAR
- MMXSupport*: BOOLEAN;
- SSESupport*: BOOLEAN;
- SSE2Support*: BOOLEAN;
- SSE3Support-: BOOLEAN; (* PH 04/11*)
- SSSE3Support-: BOOLEAN;
- SSE41Support-: BOOLEAN;
- SSE42Support-: BOOLEAN;
- SSE5Support-: BOOLEAN;
- AVXSupport-: BOOLEAN;
- version*: ARRAY 64 OF CHAR; (** Aos version *)
- features*,features2*: SET; (** processor features *)
- 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 *)
- boottime-: HUGEINT; (** in timer units *)
- commandLine-: ARRAY 256 OF CHAR;
- hin, hout: Kernel32.HANDLE;
- VAR
- lock-: ARRAY MaxLocks OF CHAR; (* not implemented as SET because of shared access *)
- cs: ARRAY MaxLocks OF Kernel32.CriticalSection;
- trace: ARRAY 2 OF CHAR;
- defaultConfigFile, userConfigFile, traceName: ARRAY Kernel32.MaxPath OF CHAR;
- gcThreshold-: SIZE;
- bootHeapAdr: ADDRESS; (* initialized by linker, variable name must not be changed, see Win32.Aos.Link *)
- bootHeapSize: SIZE; (* initialized by linker, variable name must not be changed, see Win32.Aos.Link *)
- memBlockHead-{UNTRACED}, memBlockTail-{UNTRACED}: MemoryBlock; (* head and tail of sorted list of memory blocks *)
- (** 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;
- (** -- Atomic operations -- *)
- (** Atomic INC with one parameter. *)
- PROCEDURE -Inc*( VAR x: LONGINT );
- CODE {SYSTEM.i386}
- POP EAX
- LOCK
- INC DWORD[EAX]
- END Inc;
- (** Atomic EXCL. *)
- PROCEDURE Excl*( VAR s: SET; bit: LONGINT );
- CODE {SYSTEM.i386}
- MOV EAX, [EBP+bit]
- MOV EBX, [EBP+s]
- LOCK
- BTR [EBX], EAX
- END Excl;
- (** -- Miscellaneous -- *)
- (** This procedure should be called in all spin loops as a hint to the processor (e.g. Pentium 4). *)
- PROCEDURE -SpinHint*;
- CODE {SYSTEM.i386}
- XOR ECX, ECX ; just in case some processor interprets REP this way
- REP NOP ; PAUSE instruction (* NOP on pre-P4 processors, Spin Loop Hint on P4 and after *)
- END SpinHint;
- (* Return current instruction pointer *)
- PROCEDURE CurrentPC* (): ADDRESS;
- CODE {SYSTEM.i386}
- MOV EAX, [EBP+4]
- END CurrentPC;
- (* Return current frame pointer *)
- PROCEDURE -CurrentBP* (): ADDRESS;
- CODE {SYSTEM.i386}
- MOV EAX, EBP
- END CurrentBP;
- (* Set current frame pointer *)
- PROCEDURE -SetBP* (bp: ADDRESS);
- CODE {SYSTEM.i386}
- POP EBP
- END SetBP;
- (* Return current stack pointer *)
- PROCEDURE -CurrentSP* (): ADDRESS;
- CODE {SYSTEM.i386}
- MOV EAX, ESP
- END CurrentSP;
- (* Set current stack pointer *)
- PROCEDURE -SetSP* (sp: ADDRESS);
- CODE {SYSTEM.i386}
- POP ESP
- END SetSP;
- (** Fill "size" bytes at "destAdr" with "filler". "size" must be multiple of 4. *)
- PROCEDURE Fill32*( destAdr, 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;
- (** -- 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;
- (** 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;
- PROCEDURE GetConfig*( CONST name: ARRAY OF CHAR; VAR val: ARRAY OF CHAR );
- CONST ConfigKey = "Configuration";
- BEGIN
- COPY ("", val);
- IF Kernel32.GetPrivateProfileString (ConfigKey, name, "", val, LEN (val), userConfigFile) # 0 THEN
- ELSIF Kernel32.GetPrivateProfileString (ConfigKey, name, "", val, LEN (val), defaultConfigFile) # 0 THEN
- END;
- END GetConfig;
- PROCEDURE Shutdown*( restart: BOOLEAN );
- BEGIN
- RemoveTraceFile;
- Kernel32.Shutdown( 0 ); (* calls the finalizer of Heaps *)
- END Shutdown;
- (* Dan: from new Machine *)
- PROCEDURE -GetTimer*(): HUGEINT;
- CODE {SYSTEM.Pentium}
- RDTSC ; set EDX:EAX
- END GetTimer;
- (* Dan: mono CPU PCs *)
- PROCEDURE 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;
- (* 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 not allowed *)
- END;
- END SetupSSE2Ext;
- PROCEDURE ReadCommandLine(VAR commandLine: ARRAY OF CHAR);
- VAR adr: ADDRESS; i: LONGINT; ch: CHAR;
- BEGIN
- adr := Kernel32.GetCommandLine();
- SYSTEM.GET(adr,ch);
- i := 0;
- WHILE (i<LEN(commandLine)-1) & (ch # 0X) DO
- commandLine[i] := ch;
- INC(adr); INC(i);
- SYSTEM.GET(adr,ch);
- END;
- END ReadCommandLine;
- PROCEDURE ParseLine(VAR c: ARRAY OF CHAR; VAR iniFile: ARRAY OF CHAR);
- VAR i: LONGINT;
- PROCEDURE SkipSpaces;
- BEGIN
- WHILE (c[i] <= " ") & (c[i] # 0X) DO INC(i) END;
- END SkipSpaces;
- PROCEDURE SkipName;
- BEGIN
- WHILE (c[i] > " ") DO INC(i) END;
- END SkipName;
- PROCEDURE CheckName(CONST name: ARRAY OF CHAR): BOOLEAN;
- VAR j: LONGINT;
- BEGIN
- j := 0;
- WHILE (c[i] = name[j]) & (c[i] # 0X) & (name[j] # 0X) DO
- INC(i); INC(j);
- END;
- RETURN (name[j] = 0X);
- END CheckName;
- PROCEDURE ReadName(VAR name: ARRAY OF CHAR);
- VAR j: LONGINT;
- BEGIN
- SkipSpaces;
- j := 0;
- WHILE (c[i] > " ") & (j < LEN(name)-1) DO
- name[j] := c[i];
- INC(i); INC(j);
- END;
- name[j] := 0X;
- END ReadName;
- BEGIN
- c[LEN(c)-1] := 0X;
- i := 0;
- SkipSpaces;
- SkipName;
- SkipSpaces;
- IF c[i] = "-" THEN (* option *)
- INC(i);
- IF CheckName("ini") THEN SkipSpaces; ReadName(iniFile) END;
- END;
- END ParseLine;
- PROCEDURE TraceChar(c: CHAR);
- VAR len: LONGINT;
- BEGIN
- len := 1;
- Kernel32.WriteFile(hout,c,len,len,NIL);
- END TraceChar;
- PROCEDURE SetTraceFile(VAR filename: ARRAY OF CHAR);
- BEGIN
- Trace.String("trace -> file "); Trace.String(filename); Trace.Ln;
- hout := Kernel32.CreateFile(filename, {Kernel32.GenericWrite}, {Kernel32.FileShareRead}, NIL, Kernel32.CreateAlways, {Kernel32.FileAttributeNormal}, Kernel32.NULL);
- Kernel32.GetFullPathName(filename, LEN(filename), filename, NIL);
- Trace.Char := TraceChar;
- END SetTraceFile;
- PROCEDURE SetTraceConsole;
- VAR res: LONGINT;
- BEGIN
- Trace.String("trace -> console"); Trace.Ln;
- res := Kernel32.AllocConsole ();
- hin := Kernel32.GetStdHandle (Kernel32.STDInput);
- hout := Kernel32.GetStdHandle (Kernel32.STDOutput);
- Trace.Char := TraceChar;
- END SetTraceConsole;
- PROCEDURE SetupTraceName(VAR traceName: ARRAY OF CHAR);
- VAR
- ext: ARRAY 256 OF CHAR;
- extPos,i,j: LONGINT;
- systemTime: Kernel32.SystemTime;
- ch: CHAR;
- PROCEDURE AppendDecimals(int: LONGINT; from, to: LONGINT);
- VAR ten: LONGINT;
- BEGIN
- WHILE to >= from DO
- traceName[i] := CHR(ORD("0")+ int DIV to MOD 10); INC(i);
- to := to DIV 10;
- END;
- END AppendDecimals;
- BEGIN
- Kernel32.GetLocalTime(systemTime);
- extPos := 0;
- REPEAT
- ch := traceName[i];
- IF ch = "." THEN j := 0; extPos := i END;
- ext[j] := ch;
- INC(j); INC(i);
- UNTIL ch = 0X;
- IF extPos > 0 THEN i := extPos END;
- ext[j] := 0X;
- AppendDecimals(systemTime.wYear,1,1000);
- AppendDecimals(systemTime.wMonth,1,10);
- AppendDecimals(systemTime.wDay,1,10);
- traceName[i] := "_"; INC(i);
- AppendDecimals(systemTime.wHour,1,10);
- AppendDecimals(systemTime.wMinute,1,10);
- AppendDecimals(systemTime.wSecond,1,10);
- traceName[i] := "_"; INC(i);
- AppendDecimals(systemTime.wMilliseconds,10,100);
- j := 0;
- REPEAT
- ch := ext[j];
- traceName[i] := ch;
- INC(i); INC(j);
- UNTIL ch = 0X;
- END SetupTraceName;
- PROCEDURE RemoveTraceFile;
- VAR res: LONGINT;
- BEGIN
- IF traceName[0] # 0X THEN
- Trace.String("removing "); Trace.String(traceName); Trace.Ln;
- Trace.Char := LogChar;
- res := Kernel32.CloseHandle(hout);
- IF res = 0 THEN
- res := Kernel32.GetLastError();
- Trace.String("could not close "); Trace.String("; res = "); Trace.Int(res,1); Trace.Ln;
- END;
- res := Kernel32.DeleteFile(traceName);
- IF res = 0 THEN
- res := Kernel32.GetLastError();
- Trace.String("could not delete "); Trace.String(traceName); Trace.String("; res = "); Trace.Int(res,1); Trace.Ln;
- END;
- END;
- END RemoveTraceFile;
- PROCEDURE ToExecutablePath(CONST name: ARRAY OF CHAR; VAR fullName: ARRAY OF CHAR);
- VAR i,j: LONGINT;
- BEGIN
- Kernel32.GetModuleFileName(Kernel32.hInstance, fullName, LEN( fullName ) );
- j := -1; i := 0;
- WHILE fullName[i] # 0X DO
- IF fullName[i] = '\' THEN j := i END;
- INC( i )
- END;
- i := 0; INC(j);
- WHILE name[i] # 0X DO
- fullName[j] := name[i]; INC(i); INC(j);
- END;
- fullName[j] := 0X;
- END ToExecutablePath;
- PROCEDURE Init*;
- VAR vendor: Vendor; ver: LONGINT; hfile: Kernel32.HANDLE;
- BEGIN
- Kernel32.Init;
- trace[1] := 0X; Trace.Char := LogChar; Trace.Color := TraceColor;
- InitLocks();
- Trace.String("Machine");
- boottime:=GetTimer();
- COPY( Version, version );
- CPUID(vendor, ver, features,features2); SetupSSE2Ext;
- fcr := (FCR() - {0,2,3,10,11}) + {0..5,8,9}; (* default FCR RC=00B *)
- ReadCommandLine(commandLine);
- ToExecutablePath(DefaultConfigFile, defaultConfigFile);
- COPY(UserConfigFile, userConfigFile);
- hfile := Kernel32.CreateFile( userConfigFile, {Kernel32.GenericRead}, {Kernel32.FileShareRead}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeNormal}, 0 );
- IF hfile = Kernel32.InvalidHandleValue THEN
- ToExecutablePath(UserConfigFile, userConfigFile);
- ELSE
- Kernel32.CloseHandle(hfile)
- END;
- (* ever used ? *)
- ParseLine(commandLine, userConfigFile);
- userConfigFile[Kernel32.GetFullPathName (userConfigFile, Kernel32.MaxPath, userConfigFile, 0)] := 0X;
- Trace.String("config file = "); Trace.String(defaultConfigFile); Trace.Ln;
- Trace.String("user config file = "); Trace.String(userConfigFile); Trace.Ln;
- traceName[0] := 0X;
- GetConfig("Trace",traceName);
- IF traceName = "File" THEN
- traceName := "SystemTrace.txt";
- SetupTraceName(traceName);
- SetTraceFile(traceName);
- ELSIF traceName = "Console" THEN SetTraceConsole
- (* else trace is on kernel log *)
- END;
- END Init;
- (* Initialize locks. *)
- PROCEDURE InitLocks;
- VAR i: LONGINT;
- BEGIN
- i := 0;
- WHILE i < MaxLocks DO Kernel32.InitializeCriticalSection( cs[i] ); lock[i] := "N"; INC( i ) END;
- END InitLocks;
- PROCEDURE CleanupLocks*;
- VAR i: LONGINT;
- BEGIN
- i := 0;
- WHILE i < MaxLocks DO Kernel32.DeleteCriticalSection( cs[i] ); INC( i ) END;
- END CleanupLocks;
- (** Acquire a spin-lock. *)
- PROCEDURE Acquire*( level: LONGINT ); (* non reentrant lock (non reentrance "ensured" by ASSERT statement ), CriticalSections are reentrant *)
- BEGIN
- Kernel32.EnterCriticalSection( cs[level] );
- IF StrongChecks THEN
- ASSERT ( lock[level] = "N", 1001 );
- ELSIF lock[level] # "N" THEN
- Trace.String("warning: reentered non-reentrant lock"); Trace.Ln;
- END;
- lock[level] := "Y";
- END Acquire;
- (** Release a spin-lock. *)
- PROCEDURE Release*( level: LONGINT ); (* release lock *)
- BEGIN
- IF StrongChecks THEN
- ASSERT ( lock[level] ="Y", 1002 );
- ELSIF lock[level] # "Y" THEN
- Trace.String("warning: reentered non-reentrant lock"); Trace.Ln;
- END;
- lock[level] := "N";
- Kernel32.LeaveCriticalSection( cs[level] )
- END Release;
- (* added by Alexey *)
- PROCEDURE GetMemStatus(VAR stat: Kernel32.MemoryStatusEx): BOOLEAN;
- BEGIN
- stat.dwLength := 64;
- IF Kernel32.GlobalMemoryStatusEx(stat) = 1 THEN
- RETURN TRUE;
- ELSE
- RETURN FALSE;
- END;
- END GetMemStatus;
- (** dummy procedure to make GC work for both I386 and Win32 *)
- PROCEDURE GetKernelStacks*(VAR stack: ARRAY OF Stack);
- VAR i: LONGINT;
- BEGIN
- FOR i := 0 TO MaxCPU-1 DO
- stack[i].adr := NilVal;
- stack[i].high := NilVal
- END
- END GetKernelStacks;
- (* 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;
- 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 := SYSTEM.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 *)
- SYSTEM.PUT(memBlockAdr + HeapBlockOffset, memHeaderAdr + BlockHeaderSize); (* set reference to header part of memory block correctly *)
- SYSTEM.PUT(memBlockAdr + TypeDescOffset, 0); (* set type descriptor field of memory block to default value, memory blocks are not traced by GC *)
- SYSTEM.PUT(memHeaderAdr + BlockHeaderSize + DataAdrOffset, memBlockAdr); (* set dataAdr of RecordBlockDesc to correct value *)
- SYSTEM.PUT(memHeaderAdr + BlockHeaderSize + 2*AddressSize , memBlkSize);
- (* fill first heap block *)
- SYSTEM.PUT(beginBlockAdr,0);
- SYSTEM.PUT(beginBlockAdr+AddressSize,0);
- SYSTEM.PUT(beginBlockAdr+2*AddressSize,0);
- SYSTEM.PUT(beginBlockAdr+3*AddressSize,beginBlockAdr+7*AddressSize);
- SYSTEM.PUT(beginBlockAdr+4*AddressSize,endBlockAdr-beginBlockAdr);
- SYSTEM.PUT(beginBlockAdr+5*AddressSize,beginBlockAdr+2*AddressSize);
- SYSTEM.PUT(beginBlockAdr+6*AddressSize,0);
- memoryBlock := memBlock;
- END InitHeap;
- (** 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;
- BEGIN
- RETURN (p >= memBlockHead.beginBlockAdr) & (p <= memBlockTail.endBlockAdr)
- OR (p>=401000H) & (p<=500000H) (*! guess until kernel size known *)
- END ValidHeapAddress;
- PROCEDURE GetFreeK* (VAR total, lowFree, highFree: SIZE);
- VAR
- stat: Kernel32.MemoryStatusEx;
- BEGIN
- total := MAX(LONGINT); lowFree := 0; highFree := total;
- (*<< added by Alexey *)
- IF GetMemStatus(stat) THEN
- total := SHORT(stat.ullTotalVirtual DIV 1024);
- lowFree := 0;
- highFree := SHORT(stat.ullAvailVirtual DIV 1024);
- END;
- (* added by Alexey >>*)
- END GetFreeK;
- (* ug *)
- PROCEDURE TraceMemBlocks*;
- VAR memBlock {UNTRACED}: MemoryBlock; i : LONGINT;
- BEGIN
- memBlock := memBlockHead;
- i := 0;
- WHILE memBlock # NIL DO
- Trace.String("block "); Trace.Int(i, 0); Trace.String(": startAdr = "); Trace.Hex(memBlock.startAdr, 0);
- Trace.String(" size = "); Trace.Hex(memBlock.size, 0);
- Trace.String(" beginBlockAdr = "); Trace.Hex(memBlock.beginBlockAdr, 0);
- Trace.String(" endBlockAdr = "); Trace.Hex(memBlock.endBlockAdr, 0); Trace.Ln;
- memBlock := memBlock.next;
- INC(i)
- END
- END TraceMemBlocks;
- (* 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 *)
- memBlock.next := cur;
- prev.next := memBlock;
- IF cur = NIL THEN
- memBlockTail := memBlock
- END
- END
- END InsertMemoryBlock;
- (* expand heap by allocating a new memory block - called during GC *)
- PROCEDURE ExpandHeap*(dummy: LONGINT; size: SIZE; 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; initVal: LONGINT;
- continue: BOOLEAN;
- BEGIN
- 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 *)
- INC(memBlkSize, (-memBlkSize) MOD MemBlockSize);
- initVal := memBlockTail.startAdr + memBlockTail.size;
- 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;
- continue := adr = initVal;
- Trace.String("expand heap block intVal "); Trace.Int(initVal,1); Trace.Ln;
- Trace.String("expand heap block memBlkSize "); Trace.Int(memBlkSize,1); Trace.Ln;
- Trace.String("expand heap block adr "); Trace.Int(adr,1); Trace.Ln;
- ASSERT(adr # 0);
- IF adr # 0 THEN
- alignOffset := (-adr) MOD StaticBlockSize;
- IF continue THEN
- memBlock := memBlockTail;
- memBlock.size := memBlock.size + memBlkSize;
- beginBlockAdr := memBlockTail.endBlockAdr;
- endBlockAdr := beginBlockAdr;
- INC(endBlockAdr, memBlkSize);
- ELSE
- memHeaderAdr := adr + alignOffset; (* force alignment of memory block start *)
- memBlockAdr := memHeaderAdr + MemBlockHeaderSize;
- memBlock := SYSTEM.VAL(MemoryBlock, memBlockAdr);
- memBlock.next := NIL;
- memBlock.startAdr := adr;
- memBlock.size := memBlkSize;
- beginBlockAdr := memHeaderAdr + memDescSize;
- endBlockAdr := adr + memBlkSize - alignOffset;
- memBlock.beginBlockAdr := beginBlockAdr;
- memBlock.endBlockAdr := beginBlockAdr;
- (* upon memory block insertion memBlock.beginBlockAdr = memBlock.endBlockAdr to denote that the memory block has no valid heap blocks yet
- - necessary for real-time GC. Memory block end address is set by caller by using SetMemBlockEndAddress after fitting free block in. *)
- (* copy header of memBlockHead to header of memBlock - byte by byte *)
- memBlockHeadAdr := SYSTEM.VAL(ADDRESS, memBlockHead);
- FOR i := 0 TO MemBlockHeaderSize - 1 DO
- SYSTEM.GET(memBlockHeadAdr - MemBlockHeaderSize + i, ch);
- SYSTEM.PUT(memBlockAdr - MemBlockHeaderSize + i, ch)
- END;
- (* correct fields *)
- SYSTEM.PUT(memBlockAdr + HeapBlockOffset, memHeaderAdr + BlockHeaderSize); (* set reference to header part of memory block correctly *)
- SYSTEM.PUT(memBlockAdr + TypeDescOffset, 0); (* set type descriptor field of memory block to default value, memory blocks are not traced by GC *)
- SYSTEM.PUT(memHeaderAdr + BlockHeaderSize + DataAdrOffset, memBlockAdr); (* set dataAdr of RecordBlockDesc to correct value *)
- InsertMemoryBlock(memBlock);
- END;
- memoryBlock := memBlock;
- ELSE
- beginBlockAdr := 0; endBlockAdr := 0;
- END;
- END ExpandHeap;
- (* Set memory block end address *)
- PROCEDURE SetMemoryBlockEndAddress*(memBlock: MemoryBlock; endBlockAdr: ADDRESS);
- BEGIN
- ASSERT(endBlockAdr >= memBlock.beginBlockAdr);
- memBlock.endBlockAdr := endBlockAdr
- END SetMemoryBlockEndAddress;
- (* Free unused memory block - called during GC *)
- PROCEDURE FreeMemBlock*(memBlock: MemoryBlock);
- VAR cur {UNTRACED}, prev {UNTRACED}: MemoryBlock;
- startAdr: ADDRESS;
- BEGIN
- cur := memBlockHead;
- prev := NIL;
- WHILE (cur # NIL) & (cur # memBlock) DO
- prev := cur;
- cur := cur.next
- END;
- IF cur = memBlock THEN
- IF prev = NIL THEN
- memBlockHead := cur.next;
- ELSE
- prev.next := cur.next;
- IF prev.next = NIL THEN
- memBlockTail := prev
- END
- END;
- memBlock.next := NIL;
- startAdr := memBlock.startAdr; (* this value must be cached for the second call of Kernel32.VirtualFree *)
- Kernel32.VirtualFree(SYSTEM.VAL(LONGINT, memBlock.startAdr), memBlock.size, {Kernel32.MEMDecommit});
- Kernel32.VirtualFree(SYSTEM.VAL(LONGINT, startAdr ), 0, {Kernel32.MEMRelease});
- ELSE
- HALT(535) (* error in memory block management *)
- END;
- END FreeMemBlock;
- PROCEDURE PhysicalAdr*(adr: ADDRESS; size: SIZE): ADDRESS;
- END PhysicalAdr;
- (** -- 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 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 -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;
- (* function returning the number of processors that are available to Aos *)
- PROCEDURE NumberOfProcessors*( ): LONGINT;
- VAR info: Kernel32.SystemInfo;
- BEGIN
- Kernel32.GetSystemInfo( info );
- RETURN info.dwNumberOfProcessors
- END NumberOfProcessors;
- (* function for changing byte order *)
- PROCEDURE ChangeByteOrder* (n: LONGINT): LONGINT;
- CODE { SYSTEM.Pentium }
- MOV EAX, [EBP+n] ; load n in eax
- BSWAP EAX ; swap byte order
- END ChangeByteOrder;
- PROCEDURE TraceColor (c: SHORTINT);
- END TraceColor;
- PROCEDURE LogChar (c: CHAR);
- BEGIN trace[0] := c; Kernel32.OutputString (trace);
- END LogChar;
- PROCEDURE -GetEAX*(): LONGINT;
- CODE{SYSTEM.i386}
- END GetEAX;
- PROCEDURE -GetECX*(): LONGINT;
- CODE{SYSTEM.i386}
- MOV EAX,ECX
- END GetECX;
- PROCEDURE -SetEAX*(n: LONGINT);
- CODE{SYSTEM.i386} POP EAX
- END SetEAX;
- PROCEDURE -SetEBX*(n: LONGINT);
- CODE{SYSTEM.i386}
- POP EBX
- END SetEBX;
- PROCEDURE -SetECX*(n: LONGINT);
- CODE{SYSTEM.i386}
- POP ECX
- END SetECX;
- PROCEDURE -SetEDX*(n: LONGINT);
- CODE{SYSTEM.i386}
- POP EDX
- END SetEDX;
- PROCEDURE -SetESI*(n: LONGINT);
- CODE{SYSTEM.i386}
- POP ESI
- END SetESI;
- PROCEDURE -SetEDI*(n: LONGINT);
- CODE{SYSTEM.i386}
- POP EDI
- END SetEDI;
- PROCEDURE Portin8*(port: LONGINT; VAR val: CHAR);
- CODE{SYSTEM.i386}
- MOV EDX,[EBP+port]
- IN AL, DX
- MOV ECX, [EBP+val]
- MOV [ECX], AL
- END Portin8;
- PROCEDURE Portin16*(port: LONGINT; VAR val: INTEGER);
- CODE{SYSTEM.i386}
- MOV EDX,[EBP+port]
- IN AX, DX
- MOV ECX, [EBP+val]
- MOV [ECX], AX
- END Portin16;
- PROCEDURE Portin32*(port: LONGINT; VAR val: LONGINT);
- CODE{SYSTEM.i386}
- MOV EDX,[EBP+port]
- IN EAX, DX
- MOV ECX, [EBP+val]
- MOV [ECX], EAX
- END Portin32;
- PROCEDURE Portout8*(port: LONGINT; val: CHAR);
- CODE{SYSTEM.i386}
- MOV AL,[EBP+val]
- MOV EDX,[EBP+port]
- OUT DX,AL
- END Portout8;
- PROCEDURE Portout16*(port: LONGINT; val: INTEGER);
- CODE{SYSTEM.i386}
- MOV AX,[EBP+val]
- MOV EDX,[EBP+port]
- OUT DX,AX
- END Portout16;
- PROCEDURE Portout32*(port: LONGINT; val: LONGINT);
- CODE{SYSTEM.i386}
- MOV EAX,[EBP+val]
- MOV EDX,[EBP+port]
- OUT DX,EAX
- END Portout32;
- BEGIN
- IF ~Kernel32.Generic THEN
- Init
- END;
- END Machine.
|