{ **************************************************************************** This file is part of the Free Pascal run time library. Copyright (c) 1999-2002 by Free Pascal development team Free Pascal - EMX runtime library See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ****************************************************************************} unit System; interface {Link the startup code.} {$l prt1.o} {$I systemh.inc} const LineEnding = #13#10; { LFNSupport is defined separately below!!! } DirectorySeparator = '\'; DriveSeparator = ':'; ExtensionSeparator = '.'; PathSeparator = ';'; AllowDirectorySeparators : set of char = ['\','/']; AllowDriveSeparators : set of char = [':']; { FileNameCaseSensitive is defined separately below!!! } maxExitCode = 255; MaxPathLen = 256; AllFilesMask = '*'; type Tos=(osDOS,osOS2,osDPMI); var os_mode:Tos; first_meg:pointer; type TByteArray = array [0..$ffff] of byte; PByteArray = ^TByteArray; TSysThreadIB = record TID, Priority, Version: cardinal; MCCount, MCForceFlag: word; end; PSysThreadIB = ^TSysThreadIB; TThreadInfoBlock = record PExChain, Stack, StackLimit: pointer; TIB2: PSysThreadIB; Version, Ordinal: cardinal; end; PThreadInfoBlock = ^TThreadInfoBlock; PPThreadInfoBlock = ^PThreadInfoBlock; TProcessInfoBlock = record PID, ParentPid, Handle: cardinal; Cmd, Env: PByteArray; Status, ProcType: cardinal; end; PProcessInfoBlock = ^TProcessInfoBlock; PPProcessInfoBlock = ^PProcessInfoBlock; const UnusedHandle=-1; StdInputHandle=0; StdOutputHandle=1; StdErrorHandle=2; LFNSupport: boolean = true; FileNameCaseSensitive: boolean = false; CtrlZMarksEOF: boolean = true; (* #26 is considered as end of file *) sLineBreak = LineEnding; DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF; var { C-compatible arguments and environment } argc : longint;external name '_argc'; argv : ppchar;external name '_argv'; envp : ppchar;external name '_environ'; EnvC: cardinal; external name '_envc'; (* Pointer to the block of environment variables - used e.g. in unit Dos. *) Environment: PChar; var (* Type / run mode of the current process: *) (* 0 .. full screen OS/2 session *) (* 1 .. DOS session *) (* 2 .. VIO windowable OS/2 session *) (* 3 .. Presentation Manager OS/2 session *) (* 4 .. detached (background) OS/2 process *) ApplicationType: cardinal; procedure SetDefaultOS2FileType (FType: ShortString); procedure SetDefaultOS2Creator (Creator: ShortString); implementation {$I system.inc} var heap_base: pointer; external name '__heap_base'; heap_brk: pointer; external name '__heap_brk'; heap_end: pointer; external name '__heap_end'; (* Maximum heap size - only used if heap is allocated as continuous block. *) {$IFDEF CONTHEAP} BrkLimit: cardinal; {$ENDIF CONTHEAP} {**************************************************************************** Miscellaneous related routines. ****************************************************************************} {$asmmode intel} procedure system_exit; assembler; asm mov ah, 04ch mov al, byte ptr exitcode call syscall end {['EAX']}; {$ASMMODE ATT} function paramcount:longint;assembler; asm movl argc,%eax decl %eax end {['EAX']}; function args:pointer;assembler; asm movl argv,%eax end {['EAX']}; function paramstr(l:longint):string; var p:^Pchar; begin { There seems to be a problem with EMX for DOS when trying to } { access paramstr(0), and to avoid problems between DOS and } { OS/2 they have been separated. } if os_Mode = OsOs2 then begin if L = 0 then begin GetMem (P, 260); p[0] := #0; { in case of error, initialize to empty string } {$ASMMODE INTEL} asm mov edx, P mov ecx, 260 mov eax, 7F33h call syscall { error handle already with empty string } end ['eax', 'ecx', 'edx']; ParamStr := StrPas (PChar (P)); FreeMem (P, 260); end else if (l>0) and (l<=paramcount) then begin p:=args; paramstr:=strpas(p[l]); end else paramstr:=''; end else begin p:=args; paramstr:=strpas(p[l]); end; end; procedure randomize; assembler; asm mov ah, 2Ch call syscall mov word ptr [randseed], cx mov word ptr [randseed + 2], dx end {['eax', 'ecx', 'edx']}; {$ASMMODE ATT} {***************************************************************************** System unit initialization. ****************************************************************************} {**************************************************************************** Error Message writing using messageboxes ****************************************************************************} type TWinMessageBox = function (Parent, Owner: cardinal; BoxText, BoxTitle: PChar; Identity, Style: cardinal): cardinal; cdecl; TWinInitialize = function (Options: cardinal): cardinal; cdecl; TWinCreateMsgQueue = function (Handle: cardinal; cmsg: longint): cardinal; cdecl; const ErrorBufferLength = 1024; mb_OK = $0000; mb_Error = $0040; mb_Moveable = $4000; MBStyle = mb_OK or mb_Error or mb_Moveable; WinInitialize: TWinInitialize = nil; WinCreateMsgQueue: TWinCreateMsgQueue = nil; WinMessageBox: TWinMessageBox = nil; EnvSize: cardinal = 0; var ErrorBuf: array [0..ErrorBufferLength] of char; ErrorLen: longint; PMWinHandle: cardinal; function ErrorWrite (var F: TextRec): integer; { An error message should always end with #13#10#13#10 } var P: PChar; I: longint; begin if F.BufPos > 0 then begin if F.BufPos + ErrorLen > ErrorBufferLength then I := ErrorBufferLength - ErrorLen else I := F.BufPos; Move (F.BufPtr^, ErrorBuf [ErrorLen], I); Inc (ErrorLen, I); ErrorBuf [ErrorLen] := #0; end; if ErrorLen > 3 then begin P := @ErrorBuf [ErrorLen]; for I := 1 to 4 do begin Dec (P); if not (P^ in [#10, #13]) then break; end; end; if ErrorLen = ErrorBufferLength then I := 4; if (I = 4) then begin WinMessageBox (0, 0, @ErrorBuf, PChar ('Error'), 0, MBStyle); ErrorLen := 0; end; F.BufPos := 0; ErrorWrite := 0; end; function ErrorClose (var F: TextRec): integer; begin if ErrorLen > 0 then begin WinMessageBox (0, 0, @ErrorBuf, PChar ('Error'), 0, MBStyle); ErrorLen := 0; end; ErrorLen := 0; ErrorClose := 0; end; function ErrorOpen (var F: TextRec): integer; begin TextRec(F).InOutFunc := @ErrorWrite; TextRec(F).FlushFunc := @ErrorWrite; TextRec(F).CloseFunc := @ErrorClose; ErrorOpen := 0; end; procedure AssignError (var T: Text); begin Assign (T, ''); TextRec (T).OpenFunc := @ErrorOpen; Rewrite (T); end; procedure DosEnvInit; var Q: PPChar; I: cardinal; begin (* It's a hack, in fact - DOS stores the environment the same way as OS/2 does, but I don't know how to find Program Segment Prefix and thus the environment address under EMX, so I'm recreating this structure using EnvP pointer. *) {$ASMMODE INTEL} asm cld mov ecx, EnvC mov esi, EnvP xor eax, eax xor edx, edx @L1: xchg eax, edx push ecx mov ecx, -1 mov edi, [esi] repne scasb neg ecx dec ecx xchg eax, edx add eax, ecx pop ecx dec ecx jecxz @Stop inc esi inc esi inc esi inc esi jmp @L1 @Stop: inc eax mov EnvSize, eax end ['eax','ecx','edx','esi','edi']; Environment := GetMem (EnvSize); asm cld mov ecx, EnvC mov edx, EnvP mov edi, Environment @L2: mov esi, [edx] @Copying: lodsb stosb or al, al jnz @Copying dec ecx jecxz @Stop2 inc edx inc edx inc edx inc edx jmp @L2 @Stop2: stosb end ['eax','ecx','edx','esi','edi']; end; procedure SysInitStdIO; begin { Setup stdin, stdout and stderr, for GUI apps redirect stderr,stdout to be displayed in a messagebox } (* StdInputHandle := longint(GetStdHandle(cardinal(STD_INPUT_HANDLE))); StdOutputHandle := longint(GetStdHandle(cardinal(STD_OUTPUT_HANDLE))); StdErrorHandle := longint(GetStdHandle(cardinal(STD_ERROR_HANDLE))); if not IsConsole then begin if (DosLoadModule (nil, 0, 'PMWIN', PMWinHandle) = 0) and (DosQueryProcAddr (PMWinHandle, 789, nil, pointer (WinMessageBox)) = 0) and (DosQueryProcAddr (PMWinHandle, 763, nil, pointer (WinInitialize)) = 0) and (DosQueryProcAddr (PMWinHandle, 716, nil, pointer (WinCreateMsgQueue)) = 0) then begin WinInitialize (0); WinCreateMsgQueue (0, 0); end else HandleError (2); AssignError (StdErr); AssignError (StdOut); Assign (Output, ''); Assign (Input, ''); end else begin *) OpenStdIO (Input, fmInput, StdInputHandle); OpenStdIO (Output, fmOutput, StdOutputHandle); OpenStdIO (ErrOutput, fmOutput, StdErrorHandle); OpenStdIO (StdOut, fmOutput, StdOutputHandle); OpenStdIO (StdErr, fmOutput, StdErrorHandle); (* end; *) end; threadvar DefaultCreator: ShortString; DefaultFileType: ShortString; procedure SetDefaultOS2FileType (FType: ShortString); begin {$WARNING Not implemented yet!} DefaultFileType := FType; end; procedure SetDefaultOS2Creator (Creator: ShortString); begin {$WARNING Not implemented yet!} DefaultCreator := Creator; end; function GetFileHandleCount: longint; var L1: longint; L2: cardinal; begin L1 := 0; (* Don't change the amount, just check. *) if DosSetRelMaxFH (L1, L2) <> 0 then GetFileHandleCount := 50 else GetFileHandleCount := L2; end; function CheckInitialStkLen (StkLen: SizeUInt): SizeUInt; begin CheckInitialStkLen := StkLen; end; var TIB: PThreadInfoBlock; PIB: PProcessInfoBlock; const FatalHeap: array [0..33] of char = 'FATAL: Cannot initialize heap!!'#13#10'$'; begin IsLibrary := FALSE; {Determine the operating system we are running on.} {$ASMMODE INTEL} asm push ebx mov os_mode, 0 mov eax, 7F0Ah call syscall test bx, 512 {Bit 9 is OS/2 flag.} setne byte ptr os_mode test bx, 4096 jz @noRSX mov os_mode, 2 @noRSX: {Enable the brk area by initializing it with the initial heap size.} mov eax, 7F01h mov edx, heap_brk add edx, heap_base call syscall cmp eax, -1 jnz @heapok lea edx, FatalHeap mov eax, 900h call syscall pop ebx push dword 204 call HandleError @heapok: {$IFDEF CONTHEAP} { Find out brk limit } mov eax, 7F02h mov ecx, 3 call syscall jcxz @heaplimitknown mov eax, 0 @heaplimitknown: mov BrkLimit, eax {$ELSE CONTHEAP} { Change sbrk behaviour to allocate arbitrary (non-contiguous) memory blocks } mov eax, 7F0Fh mov ecx, 0Ch mov edx, 8 call syscall {$ENDIF CONTHEAP} pop ebx end ['eax', 'ecx', 'edx']; { in OS/2 this will always be nil, but in DOS mode } { this can be changed. } first_meg := nil; {Now request, if we are running under DOS, read-access to the first meg. of memory.} if os_mode in [osDOS,osDPMI] then asm push ebx mov eax, 7F13h xor ebx, ebx mov ecx, 0FFFh xor edx, edx call syscall jc @endmem mov first_meg, eax @endmem: pop ebx end ['eax', 'ecx', 'edx'] else begin (* Initialize the amount of file handles *) FileHandleCount := GetFileHandleCount; end; {At 0.9.2, case for enumeration does not work.} case os_mode of osDOS: begin stackbottom:=pointer(heap_brk); {In DOS mode, heap_brk is also the stack bottom.} StackTop := StackBottom + InitialStkLen; {$WARNING To be checked/corrected!} ApplicationType := 1; (* Running under DOS. *) IsConsole := true; ProcessID := 1; ThreadID := 1; end; osOS2: begin DosGetInfoBlocks (@TIB, @PIB); StackBottom := pointer (TIB^.Stack); StackTop := TIB^.StackLimit; Environment := pointer (PIB^.Env); ApplicationType := PIB^.ProcType; ProcessID := PIB^.PID; ThreadID := TIB^.TIB2^.TID; IsConsole := ApplicationType <> 3; end; osDPMI: begin stackbottom:=nil; {Not sure how to get it, but seems to be always zero.} StackTop := StackBottom + InitialStkLen; {$WARNING To be checked/corrected!} ApplicationType := 1; (* Running under DOS. *) IsConsole := true; ProcessID := 1; ThreadID := 1; end; end; exitproc:=nil; StackLength := CheckInitialStkLen (InitialStkLen); {Initialize the heap.} initheap; { ... and exceptions } SysInitExceptions; { ... and I/O } SysInitStdIO; { no I/O-Error } inoutres:=0; InitSystemThreads; InitVariantManager; {$ifdef HASWIDESTRING} InitWideStringManager; {$endif HASWIDESTRING} if os_Mode in [osDOS,osDPMI] then DosEnvInit; {$IFDEF DUMPGROW} {$IFDEF CONTHEAP} WriteLn ('Initial brk size is ', GetHeapSize); WriteLn ('Brk limit is ', BrkLimit); {$ENDIF CONTHEAP} {$ENDIF DUMPGROW} end.