fpc/rtl/msxdos/system.pp
ondrej 1a0ba60de6 * revert r47598: implement TRandomGenerator
git-svn-id: trunk@47605 -
2020-11-27 04:53:06 +00:00

723 lines
16 KiB
ObjectPascal

unit System;
interface
{$define FPC_IS_SYSTEM}
{ The heap for MSDOS is implemented
in tinyheap.inc include file,
but it uses default SysGetMem names }
{$define HAS_MEMORYMANAGER}
{ define TEST_FPU_INT10 to force keeping local int10,
for testing purpose only }
{$DEFINE FPC_INCLUDE_SOFTWARE_MUL}
{$DEFINE FPC_INCLUDE_SOFTWARE_MOD_DIV}
{$DEFINE FPC_USE_SMALL_DEFAULTSTACKSIZE}
{ To avoid warnings in thread.inc code,
but value must be really given after
systemh.inc is included otherwise the
$mode switch is not effective }
{ Use Ansi Char for files }
{$define FPC_ANSI_TEXTFILEREC}
{$define FPC_STDOUT_TRUE_ALIAS}
{$ifdef NO_WIDESTRINGS}
{ Do NOT use wide Char for files }
{$undef FPC_HAS_FEATURE_WIDESTRINGS}
{$endif NO_WIDESTRINGS}
{$I systemh.inc}
{$I tnyheaph.inc}
{$I portsh.inc}
{$ifndef FPUNONE}
{$ifdef FPC_HAS_FEATURE_SOFTFPU}
{$define fpc_softfpu_interface}
{$i softfpu.pp}
{$undef fpc_softfpu_interface}
{$endif FPC_HAS_FEATURE_SOFTFPU}
{$endif FPUNONE}
const
LineEnding = #13#10;
{ LFNSupport is a variable here, defined below!!! }
DirectorySeparator = '\';
DriveSeparator = ':';
ExtensionSeparator = '.';
PathSeparator = ';';
AllowDirectorySeparators : set of char = ['\','/'];
AllowDriveSeparators : set of char = [':'];
{ FileNameCaseSensitive and FileNameCasePreserving are defined separately below!!! }
maxExitCode = 255;
MaxPathLen = 256;
const
{ Default filehandles }
UnusedHandle = $ffff;{ instead of -1, as it is a word value}
StdInputHandle = 0;
StdOutputHandle = 1;
{ MSX-DOS does not have a separate StdErr }
StdErrorHandle = 1;
FileNameCaseSensitive : boolean = false;
FileNameCasePreserving: boolean = false;
CtrlZMarksEOF: boolean = true; (* #26 is considered as end of file *)
sLineBreak = LineEnding;
DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF;
var
{ Mem[] support }
mem : array[0..$7fff-1] of byte absolute $0;
memw : array[0..($7fff div sizeof(word))-1] of word absolute $0;
meml : array[0..($7fff div sizeof(longint))-1] of longint absolute $0;
{ C-compatible arguments and environment }
argc:smallint; //!! public name 'operatingsystem_parameter_argc';
argv:PPchar; //!! public name 'operatingsystem_parameter_argv';
{ The DOS Program Segment Prefix segment (TP7 compatibility) }
PrefixSeg:Word;public name '__fpc_PrefixSeg';
SaveInt00: FarPointer;public name '__SaveInt00';
SaveInt10: FarPointer;public name '__SaveInt10';
SaveInt75: FarPointer;public name '__SaveInt75';
fpu_status: word;public name '__fpu_status';
const
AllFilesMask: string [3] = '*.*';
const
LFNSupport = false;
implementation
procedure DebugWrite(s: PChar); forward;
procedure DebugWrite(const S: string); forward;
procedure DebugWriteLn(const S: string); forward;
{$ifdef todo}
const
{ used for an offset fixup for accessing the proc parameters in asm routines
that use nostackframe. We can't use the parameter name directly, because
i8086 doesn't support sp relative addressing. }
{$ifdef FPC_X86_CODE_FAR}
extra_param_offset = 2;
{$else FPC_X86_CODE_FAR}
extra_param_offset = 0;
{$endif FPC_X86_CODE_FAR}
{$if defined(FPC_X86_DATA_FAR) or defined(FPC_X86_DATA_HUGE)}
extra_data_offset = 2;
{$else}
extra_data_offset = 0;
{$endif}
type
PFarByte = ^Byte;//far;
PFarChar = ^Char;//far;
PFarWord = ^Word;//far;
PPFarChar = ^PFarChar;
{$endif}
var
stklen: word; external name '__stklen';
__heapsize: Word;external name '__heapsize';
__fpc_initialheap: array[0..0] of byte;external name '__fpc_initialheap';
var
__stktop : pointer;public name '__stktop';
dos_version:Word;public name 'dos_version';
dos_env_count:smallint;public name '__dos_env_count';
dos_argv0 : PChar;public name '__fpc_dos_argv0';
{$I registers.inc}
procedure Intr(IntNo: Byte; var Regs: Registers); external name 'FPC_INTR';
procedure MsxDos(var Regs: Registers); assembler; nostackframe; public name 'FPC_MSXDOS';
asm
//in a, (0x2e)
{ store registers contents }
push AF
push BC
push DE
push HL
push IX
push IY
{ allocate an additional scratch space }
push IY
{ Regs now resides at SP + 16 }
{ IY is not used for parameters, so base everything on that;
for that we need to load the address of Regs into IY }
ld IX, 0x10
add IX, SP
ld L,(IX+0)
ld H,(IX+1)
push HL
pop IY
{ fill IX with the help of HL }
ld L,(IY+8)
ld H,(IY+9)
push HL
pop IX
ld B,(IY+1)
ld C,(IY+0)
ld D,(IY+3)
ld E,(IY+2)
// load A last
//ld A,(IY+4)
ld H,(IY+7)
ld L,(IY+6)
ld A,(IY+4)
{ store IY to scratch location }
ex (SP),IY
{ call to DOS }
call 0x0005
{ store IY to scratch and restore pointer address of Regs }
ex (SP),IY
ld (IY+1),B
ld (IY+0),C
ld (IY+3),D
ld (IY+2),E
ld (IY+4),A
// skip F
ld (IY+7),H
ld (IY+6),L
{ store IX with the help of HL }
push IX
pop HL
ld (IY+8),L
ld (IY+9),H
{ store the stored IY with the help of HL }
ex (SP),HL
ld (IY+10),L
ld (IY+11),H
{ cleanup stack }
pop IY
pop IY
pop IX
pop HL
pop DE
pop BC
pop AF
end;
procedure InstallInterruptHandlers; external name 'FPC_INSTALL_INTERRUPT_HANDLERS';
procedure RestoreInterruptHandlers; external name 'FPC_RESTORE_INTERRUPT_HANDLERS';
function CheckNullArea: Boolean; external name 'FPC_CHECK_NULLAREA';
{$I system.inc}
{$I tinyheap.inc}
{$I ports.inc}
{$ifndef FPUNONE}
{$ifdef FPC_HAS_FEATURE_SOFTFPU}
{$define fpc_softfpu_implementation}
{$i softfpu.pp}
{$undef fpc_softfpu_implementation}
{ we get these functions and types from the softfpu code }
{$define FPC_SYSTEM_HAS_float64}
{$define FPC_SYSTEM_HAS_float32}
{$define FPC_SYSTEM_HAS_flag}
{$define FPC_SYSTEM_HAS_extractFloat64Frac0}
{$define FPC_SYSTEM_HAS_extractFloat64Frac1}
{$define FPC_SYSTEM_HAS_extractFloat64Exp}
{$define FPC_SYSTEM_HAS_extractFloat64Frac}
{$define FPC_SYSTEM_HAS_extractFloat64Sign}
{$define FPC_SYSTEM_HAS_ExtractFloat32Frac}
{$define FPC_SYSTEM_HAS_extractFloat32Exp}
{$define FPC_SYSTEM_HAS_extractFloat32Sign}
{$endif FPC_HAS_FEATURE_SOFTFPU}
{$endif FPUNONE}
procedure DebugWrite(S: PChar);
var
regs: Registers;
begin
while S^ <> #0 do begin
regs.C := $02;
regs.E := Ord(S^);
MsxDos(regs);
Inc(S);
end;
end;
procedure DebugWrite(const S: string);
var
regs: Registers;
i: Byte;
begin
for i := 1 to Length(S) do begin
regs.C := $02;
regs.E := Ord(S[i]);
MsxDos(regs);
end;
end;
procedure DebugWriteLn(const S: string);
begin
DebugWrite(S);
DebugWrite(#13#10);
end;
{*****************************************************************************
ParamStr/Randomize
*****************************************************************************}
var
internal_envp : PPChar = nil;
procedure setup_environment;
{$ifdef todo}
var
env_count : smallint;
cp, dos_env: PFarChar;
{$endif}
begin
{$ifdef todo}
env_count:=0;
dos_env:=Ptr(MemW[PrefixSeg:$2C], 0);
cp:=dos_env;
while cp^<>#0 do
begin
inc(env_count);
while (cp^ <> #0) do
inc(cp); { skip to NUL }
inc(cp); { skip to next character }
end;
internal_envp := getmem((env_count+1) * sizeof(PFarChar));
cp:=dos_env;
env_count:=0;
while cp^<>#0 do
begin
internal_envp[env_count] := cp;
inc(env_count);
while (cp^ <> #0) do
inc(cp); { skip to NUL }
inc(cp); { skip to next character }
end;
internal_envp[env_count]:=nil;
dos_env_count := env_count;
if dos_version >= $300 then
begin
if cp=dos_env then
inc(cp);
inc(cp, 3);
dos_argv0 := cp;
end
else
dos_argv0 := nil;
{$endif}
end;
function envp:PPChar;public name '__fpc_envp';
begin
if not assigned(internal_envp) then
setup_environment;
envp:=internal_envp;
end;
function GetEnvVar(aName: PChar): String;
var
regs: Registers;
i: SizeInt;
begin
SetLength(Result, 255);
regs.C := $6B;
regs.HL := PtrUInt(aName);
regs.DE := PtrUInt(@Result[1]);
regs.B := 255;
regs.A := 0;
MsxDos(regs);
if regs.A = 0 then begin
i := 1;
aName := PChar(@Result[1]);
while i < 256 do begin
if aName^ = #0 then begin
SetLength(Result, i);
Break;
end;
Inc(i);
Inc(aName);
end;
end else
SetLength(Result, 0);
end;
procedure setup_arguments;
var
i: SmallInt;
pc: PChar;
quote: Char;
count: SmallInt;
arglen, argv0len: SmallInt;
argblock: PChar;
arg: PChar;
doscmd : string[129]; { Dos commandline copied from PSP, max is 128 chars +1 for terminating zero }
tmp: String;
regs: Registers;
begin
tmp := GetEnvVar('PROGRAM');
argv0len := Length(tmp);
tmp := GetEnvVar('PARAMETERS');
{$IfDef SYSTEM_DEBUG_STARTUP}
Writeln(stderr,'Dos command line is #',tmp,'# size = ',length(tmp));
{$EndIf }
{ parse dos commandline }
pc:=@tmp[1];
count:=1;
{ calc total arguments length and count }
arglen:=argv0len+1;
while pc^<>#0 do
begin
{ skip leading spaces }
while pc^ in [#1..#32] do
inc(pc);
if pc^=#0 then
break;
{ calc argument length }
quote:=' ';
while (pc^<>#0) do
begin
case pc^ of
#1..#32 :
begin
if quote<>' ' then
inc(arglen)
else
break;
end;
'"' :
begin
if quote<>'''' then
begin
if pchar(pc+1)^<>'"' then
begin
if quote='"' then
quote:=' '
else
quote:='"';
end
else
inc(pc);
end
else
inc(arglen);
end;
'''' :
begin
if quote<>'"' then
begin
if pchar(pc+1)^<>'''' then
begin
if quote='''' then
quote:=' '
else
quote:='''';
end
else
inc(pc);
end
else
inc(arglen);
end;
else
inc(arglen);
end;
inc(pc);
end;
inc(arglen); { for the null terminator }
inc(count);
end;
Writeln(stderr,'Arg count: ', count, ', size: ', arglen);
{ set argc and allocate argv }
argc:=count;
argv:=AllocMem((count+1)*SizeOf(PChar));
{ allocate a single memory block for all arguments }
argblock:=GetMem(arglen);
writeln('Allocated arg vector at ', hexstr(argv), ' and block at ', hexstr(argblock));
{ create argv[0] }
argv[0]:=argblock;
arg:=argblock+argv0len;
arg^:=#0;
Inc(arg);
pc:=@tmp[1];
count:=1;
while pc^<>#0 do
begin
{ skip leading spaces }
while pc^ in [#1..#32] do
inc(pc);
if pc^=#0 then
break;
{ copy argument }
//writeln('Setting arg ',count,' to ', hexstr(arg));
asm
in a,(0x2e)
end ['a'];
argv[count]:=arg;
quote:=' ';
while (pc^<>#0) do
begin
case pc^ of
#1..#32 :
begin
if quote<>' ' then
begin
arg^:=pc^;
inc(arg);
end
else
break;
end;
'"' :
begin
if quote<>'''' then
begin
if pchar(pc+1)^<>'"' then
begin
if quote='"' then
quote:=' '
else
quote:='"';
end
else
inc(pc);
end
else
begin
arg^:=pc^;
inc(arg);
end;
end;
'''' :
begin
if quote<>'"' then
begin
if pchar(pc+1)^<>'''' then
begin
if quote='''' then
quote:=' '
else
quote:='''';
end
else
inc(pc);
end
else
begin
arg^:=pc^;
inc(arg);
end;
end;
else
begin
arg^:=pc^;
inc(arg);
end;
end;
inc(pc);
end;
arg^:=#0;
Inc(arg);
{$IfDef SYSTEM_DEBUG_STARTUP}
Writeln(stderr,'dos arg ',count,' #',strlen(argv[count]),'#',argv[count],'#');
{$EndIf SYSTEM_DEBUG_STARTUP}
inc(count);
end;
arg:=argblock;
tmp:=GetEnvVar('PROGRAM');
pc:=@tmp[1];
while pc^ <> #0 do
begin
arg^ := pc^;
Inc(arg);
Inc(pc);
end;
for count:=0 to argc-1 do
writeln('arg ',count,' at ',hexstr(argv[count]));
end;
function paramcount : longint;
begin
if argv=nil then
setup_arguments;
paramcount := argc - 1;
end;
function paramstr(l : longint) : string;
begin
if argv=nil then
setup_arguments;
if (l>=0) and (l+1<=argc) then
paramstr:=strpas(argv[l])
else
paramstr:='';
end;
procedure randomize;
{$ifdef todo}
var
hl : longint;
regs : Registers;
{$endif}
begin
{$ifdef todo}
regs.AH:=$2C;
MsDos(regs);
hl:=regs.DX;
randseed:=hl*$10000+ regs.CX;
{$endif}
end;
{*****************************************************************************
System Dependent Exit code
*****************************************************************************}
procedure system_exit;
var
h : byte;
begin
{$ifdef todo}
RestoreInterruptHandlers;
{$endif}
for h:=0 to max_files-1 do
if openfiles[h] then
begin
{$ifdef SYSTEMDEBUG}
writeln(stderr,'file ',h,' "',opennames[h],'" not closed at exit');
{$endif SYSTEMDEBUG}
if h>=5 then
do_close(h);
end;
{$ifndef FPC_MM_TINY}
{$ifdef todo}
if not CheckNullArea then
writeln(stderr, 'Nil pointer assignment');
{$endif}
{$endif FPC_MM_TINY}
asm
ld a, exitcode
ld b, a
ld c, 0x62
call 0x0005
end;
end;
{*****************************************************************************
SystemUnit Initialization
*****************************************************************************}
procedure InitDosHeap;
begin
RegisterTinyHeapBlock_Simple_Prealigned(@__fpc_initialheap,__heapsize);
end;
procedure SysInitStdIO;
begin
OpenStdIO(Input,fmInput,StdInputHandle);
OpenStdIO(Output,fmOutput,StdOutputHandle);
OpenStdIO(ErrOutput,fmOutput,StdErrorHandle);
{$ifndef FPC_STDOUT_TRUE_ALIAS}
OpenStdIO(StdOut,fmOutput,StdOutputHandle);
OpenStdIO(StdErr,fmOutput,StdErrorHandle);
{$endif FPC_STDOUT_TRUE_ALIAS}
end;
function GetProcessID: SizeUInt;
begin
GetProcessID := PrefixSeg;
end;
function CheckInitialStkLen(stklen : SizeUInt) : SizeUInt;
begin
result := stklen;
end;
procedure InitDosVersion;
var
regs: Registers;
begin
regs.C := $6F;
regs.A := 0;
MsxDos(regs);
if regs.A <> 0 then
dos_version := 0
else if regs.B < 2 then
dos_version := $100
else
dos_version := regs.DE;
end;
begin
StackLength := stklen;
StackBottom := __stktop - stklen;
InitDosVersion;
{ for now we don't support MSX-DOS 1 }
if dos_version < $100 then
Halt($85);
{$ifdef todo}
InstallInterruptHandlers;
{$endif}
{ To be set if this is a GUI or console application }
IsConsole := TRUE;
{$ifdef FPC_HAS_FEATURE_DYNLIBS}
{ If dynlibs feature is disabled,
IsLibrary is a constant, which can thus not be set to a value }
{ To be set if this is a library and not a program }
IsLibrary := FALSE;
{$endif def FPC_HAS_FEATURE_DYNLIBS}
{ Setup heap }
InitDosHeap;
SysInitExceptions;
{$ifdef FPC_HAS_FEATURE_UNICODESTRINGS}
initunicodestringmanager;
{$endif def FPC_HAS_FEATURE_UNICODESTRINGS}
{ Setup stdin, stdout and stderr }
SysInitStdIO;
{ Setup environment and arguments }
{ Done on request only Setup_Environment; }
{ Done on request only Setup_Arguments; }
{ Reset IO Error }
InOutRes:=0;
{$ifdef FPC_HAS_FEATURE_THREADING}
InitSystemThreads;
{$endif}
end.