system.pp 18 KB

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