system.pp 18 KB

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