system.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. unit System;
  2. interface
  3. { The heap for MSDOS is implemented
  4. in tinyheap.inc include file,
  5. but it uses default SysGetMem names }
  6. {$define HAS_MEMORYMANAGER}
  7. {$DEFINE FPC_INCLUDE_SOFTWARE_MUL}
  8. {$DEFINE FPC_INCLUDE_SOFTWARE_MOD_DIV}
  9. {$DEFINE FPC_USE_SMALL_DEFAULTSTACKSIZE}
  10. { To avoid warnings in thread.inc code,
  11. but value must be really given after
  12. systemh.inc is included otherwise the
  13. $mode switch is not effective }
  14. { Use Ansi Char for files }
  15. {$define FPC_ANSI_TEXTFILEREC}
  16. {$ifdef NO_WIDESTRINGS}
  17. { Do NOT use wide Char for files }
  18. {$undef FPC_HAS_FEATURE_WIDESTRINGS}
  19. {$endif NO_WIDESTRINGS}
  20. {$I systemh.inc}
  21. {$I tnyheaph.inc}
  22. const
  23. LineEnding = #13#10;
  24. { LFNSupport is a variable here, defined below!!! }
  25. DirectorySeparator = '\';
  26. DriveSeparator = ':';
  27. ExtensionSeparator = '.';
  28. PathSeparator = ';';
  29. AllowDirectorySeparators : set of char = ['\','/'];
  30. AllowDriveSeparators : set of char = [':'];
  31. { FileNameCaseSensitive and FileNameCasePreserving are defined separately below!!! }
  32. maxExitCode = 255;
  33. MaxPathLen = 256;
  34. const
  35. { Default filehandles }
  36. UnusedHandle = $ffff;{ instead of -1, as it is a word value}
  37. StdInputHandle = 0;
  38. StdOutputHandle = 1;
  39. StdErrorHandle = 2;
  40. FileNameCaseSensitive : boolean = false;
  41. FileNameCasePreserving: boolean = false;
  42. CtrlZMarksEOF: boolean = true; (* #26 is considered as end of file *)
  43. sLineBreak = LineEnding;
  44. DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF;
  45. { Default memory segments (Tp7 compatibility) }
  46. seg0040: Word = $0040;
  47. segA000: Word = $A000;
  48. segB000: Word = $B000;
  49. segB800: Word = $B800;
  50. { The value that needs to be added to the segment to move the pointer by
  51. 64K bytes (BP7 compatibility) }
  52. SelectorInc: Word = $1000;
  53. var
  54. { Mem[] support }
  55. mem : array[0..$7fff-1] of byte absolute $0:$0;
  56. memw : array[0..($7fff div sizeof(word))-1] of word absolute $0:$0;
  57. meml : array[0..($7fff div sizeof(longint))-1] of longint absolute $0:$0;
  58. { C-compatible arguments and environment }
  59. argc:smallint; //!! public name 'operatingsystem_parameter_argc';
  60. argv:PPchar; //!! public name 'operatingsystem_parameter_argv';
  61. { The DOS Program Segment Prefix segment (TP7 compatibility) }
  62. PrefixSeg:Word;public name '__fpc_PrefixSeg';
  63. SaveInt00: FarPointer;public name '__SaveInt00';
  64. SaveInt10: FarPointer;public name '__SaveInt10';
  65. SaveInt75: FarPointer;public name '__SaveInt75';
  66. AllFilesMask: string [3];
  67. {$ifndef RTLLITE}
  68. { System info }
  69. LFNSupport : boolean;
  70. {$ELSE RTLLITE}
  71. const
  72. LFNSupport = false;
  73. {$endif RTLLITE}
  74. implementation
  75. procedure DebugWrite(const S: string); forward;
  76. procedure DebugWriteLn(const S: string); forward;
  77. const
  78. fCarry = 1;
  79. { used for an offset fixup for accessing the proc parameters in asm routines
  80. that use nostackframe. We can't use the parameter name directly, because
  81. i8086 doesn't support sp relative addressing. }
  82. {$ifdef FPC_X86_CODE_FAR}
  83. extra_param_offset = 2;
  84. {$else FPC_X86_CODE_FAR}
  85. extra_param_offset = 0;
  86. {$endif FPC_X86_CODE_FAR}
  87. {$if defined(FPC_X86_DATA_FAR) or defined(FPC_X86_DATA_HUGE)}
  88. extra_data_offset = 2;
  89. {$else}
  90. extra_data_offset = 0;
  91. {$endif}
  92. type
  93. PFarByte = ^Byte;far;
  94. PFarChar = ^Char;far;
  95. PFarWord = ^Word;far;
  96. PPFarChar = ^PFarChar;
  97. var
  98. __stktop : pointer;public name '__stktop';
  99. __stkbottom : pointer;public name '__stkbottom';
  100. __nearheap_start: pointer;public name '__nearheap_start';
  101. __nearheap_end: pointer;public name '__nearheap_end';
  102. dos_version:Word;public name 'dos_version';
  103. dos_env_count:smallint;public name '__dos_env_count';
  104. dos_argv0 : PFarChar;public name '__fpc_dos_argv0';
  105. {$I registers.inc}
  106. procedure Intr(IntNo: Byte; var Regs: Registers); external name 'FPC_INTR';
  107. procedure MsDos(var Regs: Registers); external name 'FPC_MSDOS';
  108. { invokes int 21h with the carry flag set on entry; used for the LFN functions
  109. to ensure that the carry flag is set on exit on older DOS versions which don't
  110. support them }
  111. procedure MsDos_Carry(var Regs: Registers); external name 'FPC_MSDOS_CARRY';
  112. procedure InstallInterruptHandlers; external name 'FPC_INSTALL_INTERRUPT_HANDLERS';
  113. procedure RestoreInterruptHandlers; external name 'FPC_RESTORE_INTERRUPT_HANDLERS';
  114. function CheckNullArea: Boolean; external name 'FPC_CHECK_NULLAREA';
  115. var
  116. test_fpu_jmpbuf : jmp_buf;
  117. Procedure InterceptInvalidInstruction;
  118. begin
  119. longjmp(test_fpu_jmpbuf, 1);
  120. end;
  121. { Use msdos int21 set/get Interrupt address
  122. to check if coprocessor is present }
  123. {$define FPC_SYSTEM_HAS_SYSINITFPU}
  124. Procedure SysInitFPU;
  125. var
  126. { these locals are so we don't have to hack pic code in the assembler }
  127. localfpucw: word;
  128. prevInt06 : FarPointer;
  129. begin
  130. localfpucw:=Default8087CW;
  131. asm
  132. fninit
  133. fldcw localfpucw
  134. fwait
  135. end;
  136. if Test8087 < 2 then
  137. exit;
  138. asm
  139. push es
  140. push ds
  141. { Get previous interrupt 06 handler }
  142. mov ax, $3506
  143. int $21
  144. mov word [prevInt06],bx
  145. mov dx,es
  146. mov word [prevInt06+2],dx
  147. { Install local interrupt 06 handler }
  148. {$ifdef FPC_MM_TINY}
  149. { Do not use SEG here, as this introduces a relocation that
  150. is incompatible with COM executable generation }
  151. mov dx, cs
  152. {$else FPC_MM_TINY}
  153. mov dx, SEG InterceptInvalidInstruction
  154. {$endif FPC_MM_TINY}
  155. mov ds, dx
  156. mov dx, Offset InterceptInvalidInstruction
  157. mov ax, $2506
  158. int $21
  159. pop ds
  160. pop es
  161. end;
  162. if setjmp(test_fpu_jmpbuf)=0 then
  163. begin
  164. asm
  165. db $0f, $20, $c0 { mov eax,cr0 }
  166. db $83, $c8, $20 { or $0x20,eax }
  167. db $0f, $22, $c0 { mov cr0,eax }
  168. end;
  169. //writeln(stderr,'Change of cr0 succeeded');
  170. end
  171. else
  172. begin
  173. //writeln(stderr,'Change of cr0 failed');
  174. end;
  175. { Restore previous interrupt 06 handler }
  176. asm
  177. push es
  178. mov bx,word [prevInt06]
  179. mov dx,word [prevInt06+2]
  180. mov es,dx
  181. mov ax, $2506
  182. int $21
  183. pop es
  184. end;
  185. end;
  186. {$I system.inc}
  187. {$I tinyheap.inc}
  188. procedure DebugWrite(const S: string);
  189. begin
  190. asm
  191. {$if defined(FPC_X86_DATA_FAR) or defined(FPC_X86_DATA_HUGE)}
  192. push ds
  193. lds si, S
  194. {$else}
  195. mov si, S
  196. {$endif}
  197. {$ifdef FPC_ENABLED_CLD}
  198. cld
  199. {$endif FPC_ENABLED_CLD}
  200. lodsb
  201. mov cl, al
  202. xor ch, ch
  203. jcxz @@zero_length
  204. mov ah, 2
  205. @@1:
  206. lodsb
  207. mov dl, al
  208. int 21h
  209. loop @@1
  210. @@zero_length:
  211. {$if defined(FPC_X86_DATA_FAR) or defined(FPC_X86_DATA_HUGE)}
  212. pop ds
  213. {$endif}
  214. end ['ax','bx','cx','dx','si','di'];
  215. end;
  216. procedure DebugWriteLn(const S: string);
  217. begin
  218. DebugWrite(S);
  219. DebugWrite(#13#10);
  220. end;
  221. {*****************************************************************************
  222. ParamStr/Randomize
  223. *****************************************************************************}
  224. var
  225. internal_envp : PPFarChar = nil;
  226. procedure setup_environment;
  227. var
  228. env_count : smallint;
  229. cp, dos_env: PFarChar;
  230. begin
  231. env_count:=0;
  232. dos_env:=Ptr(MemW[PrefixSeg:$2C], 0);
  233. cp:=dos_env;
  234. while cp^<>#0 do
  235. begin
  236. inc(env_count);
  237. while (cp^ <> #0) do
  238. inc(cp); { skip to NUL }
  239. inc(cp); { skip to next character }
  240. end;
  241. internal_envp := getmem((env_count+1) * sizeof(PFarChar));
  242. cp:=dos_env;
  243. env_count:=0;
  244. while cp^<>#0 do
  245. begin
  246. internal_envp[env_count] := cp;
  247. inc(env_count);
  248. while (cp^ <> #0) do
  249. inc(cp); { skip to NUL }
  250. inc(cp); { skip to next character }
  251. end;
  252. internal_envp[env_count]:=nil;
  253. dos_env_count := env_count;
  254. if dos_version >= $300 then
  255. begin
  256. if cp=dos_env then
  257. inc(cp);
  258. inc(cp, 3);
  259. dos_argv0 := cp;
  260. end
  261. else
  262. dos_argv0 := nil;
  263. end;
  264. function envp:PPFarChar;public name '__fpc_envp';
  265. begin
  266. if not assigned(internal_envp) then
  267. setup_environment;
  268. envp:=internal_envp;
  269. end;
  270. procedure setup_arguments;
  271. var
  272. I: SmallInt;
  273. pc: PChar;
  274. pfc: PFarChar;
  275. quote: Char;
  276. count: SmallInt;
  277. arglen, argv0len: SmallInt;
  278. argblock: PChar;
  279. arg: PChar;
  280. doscmd : string[129]; { Dos commandline copied from PSP, max is 128 chars +1 for terminating zero }
  281. begin
  282. { load commandline from psp }
  283. SetLength(doscmd, Mem[PrefixSeg:$80]);
  284. for I := 1 to length(doscmd) do
  285. doscmd[I] := Chr(Mem[PrefixSeg:$80+I]);
  286. doscmd[length(doscmd)+1]:=#0;
  287. {$IfDef SYSTEM_DEBUG_STARTUP}
  288. Writeln(stderr,'Dos command line is #',doscmd,'# size = ',length(doscmd));
  289. {$EndIf }
  290. { find argv0len }
  291. argv0len:=0;
  292. if dos_argv0<>nil then
  293. begin
  294. pfc:=dos_argv0;
  295. while pfc^<>#0 do
  296. begin
  297. Inc(argv0len);
  298. Inc(pfc);
  299. end;
  300. end;
  301. { parse dos commandline }
  302. pc:=@doscmd[1];
  303. count:=1;
  304. { calc total arguments length and count }
  305. arglen:=argv0len+1;
  306. while pc^<>#0 do
  307. begin
  308. { skip leading spaces }
  309. while pc^ in [#1..#32] do
  310. inc(pc);
  311. if pc^=#0 then
  312. break;
  313. { calc argument length }
  314. quote:=' ';
  315. while (pc^<>#0) do
  316. begin
  317. case pc^ of
  318. #1..#32 :
  319. begin
  320. if quote<>' ' then
  321. inc(arglen)
  322. else
  323. break;
  324. end;
  325. '"' :
  326. begin
  327. if quote<>'''' then
  328. begin
  329. if pchar(pc+1)^<>'"' then
  330. begin
  331. if quote='"' then
  332. quote:=' '
  333. else
  334. quote:='"';
  335. end
  336. else
  337. inc(pc);
  338. end
  339. else
  340. inc(arglen);
  341. end;
  342. '''' :
  343. begin
  344. if quote<>'"' then
  345. begin
  346. if pchar(pc+1)^<>'''' then
  347. begin
  348. if quote='''' then
  349. quote:=' '
  350. else
  351. quote:='''';
  352. end
  353. else
  354. inc(pc);
  355. end
  356. else
  357. inc(arglen);
  358. end;
  359. else
  360. inc(arglen);
  361. end;
  362. inc(pc);
  363. end;
  364. inc(arglen); { for the null terminator }
  365. inc(count);
  366. end;
  367. { set argc and allocate argv }
  368. argc:=count;
  369. argv:=AllocMem((count+1)*SizeOf(PChar));
  370. { allocate a single memory block for all arguments }
  371. argblock:=GetMem(arglen);
  372. { create argv[0] }
  373. argv[0]:=argblock;
  374. arg:=argblock;
  375. if dos_argv0<>nil then
  376. begin
  377. pfc:=dos_argv0;
  378. while pfc^<>#0 do
  379. begin
  380. arg^:=pfc^;
  381. Inc(arg);
  382. Inc(pfc);
  383. end;
  384. end;
  385. arg^:=#0;
  386. Inc(arg);
  387. pc:=@doscmd[1];
  388. count:=1;
  389. while pc^<>#0 do
  390. begin
  391. { skip leading spaces }
  392. while pc^ in [#1..#32] do
  393. inc(pc);
  394. if pc^=#0 then
  395. break;
  396. { copy argument }
  397. argv[count]:=arg;
  398. quote:=' ';
  399. while (pc^<>#0) do
  400. begin
  401. case pc^ of
  402. #1..#32 :
  403. begin
  404. if quote<>' ' then
  405. begin
  406. arg^:=pc^;
  407. inc(arg);
  408. end
  409. else
  410. break;
  411. end;
  412. '"' :
  413. begin
  414. if quote<>'''' then
  415. begin
  416. if pchar(pc+1)^<>'"' then
  417. begin
  418. if quote='"' then
  419. quote:=' '
  420. else
  421. quote:='"';
  422. end
  423. else
  424. inc(pc);
  425. end
  426. else
  427. begin
  428. arg^:=pc^;
  429. inc(arg);
  430. end;
  431. end;
  432. '''' :
  433. begin
  434. if quote<>'"' then
  435. begin
  436. if pchar(pc+1)^<>'''' then
  437. begin
  438. if quote='''' then
  439. quote:=' '
  440. else
  441. quote:='''';
  442. end
  443. else
  444. inc(pc);
  445. end
  446. else
  447. begin
  448. arg^:=pc^;
  449. inc(arg);
  450. end;
  451. end;
  452. else
  453. begin
  454. arg^:=pc^;
  455. inc(arg);
  456. end;
  457. end;
  458. inc(pc);
  459. end;
  460. arg^:=#0;
  461. Inc(arg);
  462. {$IfDef SYSTEM_DEBUG_STARTUP}
  463. Writeln(stderr,'dos arg ',count,' #',strlen(argv[count]),'#',argv[count],'#');
  464. {$EndIf SYSTEM_DEBUG_STARTUP}
  465. inc(count);
  466. end;
  467. end;
  468. function paramcount : longint;
  469. begin
  470. if argv=nil then
  471. setup_arguments;
  472. paramcount := argc - 1;
  473. end;
  474. function paramstr(l : longint) : string;
  475. begin
  476. if argv=nil then
  477. setup_arguments;
  478. if (l>=0) and (l+1<=argc) then
  479. paramstr:=strpas(argv[l])
  480. else
  481. paramstr:='';
  482. end;
  483. procedure randomize;
  484. var
  485. hl : longint;
  486. regs : Registers;
  487. begin
  488. regs.AH:=$2C;
  489. MsDos(regs);
  490. hl:=regs.DX;
  491. randseed:=hl*$10000+ regs.CX;
  492. end;
  493. {*****************************************************************************
  494. System Dependent Exit code
  495. *****************************************************************************}
  496. procedure system_exit;
  497. var
  498. h : byte;
  499. begin
  500. RestoreInterruptHandlers;
  501. for h:=0 to max_files-1 do
  502. if openfiles[h] then
  503. begin
  504. {$ifdef SYSTEMDEBUG}
  505. writeln(stderr,'file ',h,' "',opennames[h],'" not closed at exit');
  506. {$endif SYSTEMDEBUG}
  507. if h>=5 then
  508. do_close(h);
  509. end;
  510. {$ifndef FPC_MM_TINY}
  511. if not CheckNullArea then
  512. writeln(stderr, 'Nil pointer assignment');
  513. {$endif FPC_MM_TINY}
  514. asm
  515. mov al, byte [exitcode]
  516. mov ah, 4Ch
  517. int 21h
  518. end;
  519. end;
  520. {*****************************************************************************
  521. SystemUnit Initialization
  522. *****************************************************************************}
  523. procedure InitDosHeap;
  524. type
  525. {$if defined(FPC_X86_DATA_FAR) or defined(FPC_X86_DATA_HUGE)}
  526. TPointerArithmeticType = HugePointer;
  527. {$else}
  528. TPointerArithmeticType = Pointer;
  529. {$endif}
  530. begin
  531. RegisterTinyHeapBlock_Simple_Prealigned(__nearheap_start, TPointerArithmeticType(__nearheap_end) - TPointerArithmeticType(__nearheap_start));
  532. end;
  533. function CheckLFN:boolean;
  534. var
  535. regs : Registers;
  536. RootName : pchar;
  537. buf : array [0..31] of char;
  538. begin
  539. { Check LFN API on drive c:\ }
  540. RootName:='C:\';
  541. { Call 'Get Volume Information' ($71A0) }
  542. regs.AX:=$71a0;
  543. regs.ES:=Seg(buf);
  544. regs.DI:=Ofs(buf);
  545. regs.CX:=32;
  546. regs.DS:=Seg(RootName^);
  547. regs.DX:=Ofs(RootName^);
  548. MsDos_Carry(regs);
  549. { If carryflag=0 and LFN API bit in ebx is set then use Long file names }
  550. CheckLFN:=(regs.Flags and fCarry=0) and (regs.BX and $4000=$4000);
  551. end;
  552. procedure SysInitStdIO;
  553. begin
  554. OpenStdIO(Input,fmInput,StdInputHandle);
  555. OpenStdIO(Output,fmOutput,StdOutputHandle);
  556. OpenStdIO(ErrOutput,fmOutput,StdErrorHandle);
  557. OpenStdIO(StdOut,fmOutput,StdOutputHandle);
  558. OpenStdIO(StdErr,fmOutput,StdErrorHandle);
  559. end;
  560. function GetProcessID: SizeUInt;
  561. begin
  562. GetProcessID := PrefixSeg;
  563. end;
  564. function CheckInitialStkLen(stklen : SizeUInt) : SizeUInt;
  565. begin
  566. result := stklen;
  567. end;
  568. begin
  569. StackBottom := __stkbottom;
  570. StackLength := __stktop - __stkbottom;
  571. InstallInterruptHandlers;
  572. DetectFPU;
  573. if Test8087>0 then
  574. SysInitFPU;
  575. { To be set if this is a GUI or console application }
  576. IsConsole := TRUE;
  577. {$ifdef FPC_HAS_FEATURE_DYNLIBS}
  578. { If dynlibs feature is disabled,
  579. IsLibrary is a constant, which can thus not be set to a value }
  580. { To be set if this is a library and not a program }
  581. IsLibrary := FALSE;
  582. {$endif def FPC_HAS_FEATURE_DYNLIBS}
  583. { Setup heap }
  584. InitDosHeap;
  585. SysInitExceptions;
  586. {$ifdef FPC_HAS_FEATURE_UNICODESTRINGS}
  587. initunicodestringmanager;
  588. {$endif def FPC_HAS_FEATURE_UNICODESTRINGS}
  589. { Setup stdin, stdout and stderr }
  590. SysInitStdIO;
  591. { Setup environment and arguments }
  592. { Done on request only Setup_Environment; }
  593. { Done on request only Setup_Arguments; }
  594. {$ifndef RTLLITE}
  595. { Use LFNSupport LFN }
  596. LFNSupport:=CheckLFN;
  597. if LFNSupport then
  598. begin
  599. FileNameCasePreserving:=true;
  600. AllFilesMask := '*';
  601. end
  602. else
  603. {$endif ndef RTLLITE}
  604. AllFilesMask := '*.*';
  605. { Reset IO Error }
  606. InOutRes:=0;
  607. {$ifdef FPC_HAS_FEATURE_THREADING}
  608. InitSystemThreads;
  609. {$endif}
  610. end.