Browse Source

+ add initial RTL for MSX DOS. Simple programs are already working, but there are apparantly some code generation problems that lead to I/O as well as parameters not working correctly

git-svn-id: trunk@45600 -
svenbarth 5 years ago
parent
commit
ac8552afc2

+ 10 - 0
.gitattributes

@@ -11353,6 +11353,16 @@ rtl/msdos/sysosh.inc svneol=native#text/plain
 rtl/msdos/system.pp svneol=native#text/plain
 rtl/msdos/sysutils.pp svneol=native#text/plain
 rtl/msdos/tthread.inc svneol=native#text/plain
+rtl/msxdos/Makefile.fpc svneol=native#text/plain
+rtl/msxdos/registers.inc svneol=native#text/plain
+rtl/msxdos/rtldefs.inc svneol=native#text/plain
+rtl/msxdos/si_prc.pp svneol=native#text/pascal
+rtl/msxdos/sysdir.inc svneol=native#text/plain
+rtl/msxdos/sysfile.inc svneol=native#text/plain
+rtl/msxdos/sysheap.inc svneol=native#text/plain
+rtl/msxdos/sysos.inc svneol=native#text/plain
+rtl/msxdos/sysosh.inc svneol=native#text/plain
+rtl/msxdos/system.pp svneol=native#text/pascal
 rtl/nativent/Makefile svneol=native#text/plain
 rtl/nativent/Makefile.fpc svneol=native#text/plain
 rtl/nativent/buildrtl.lpi svneol=native#text/plain

+ 1 - 0
rtl/Makefile.fpc

@@ -52,6 +52,7 @@ dirs_win16=win16
 dirs_watcom=watcom
 dirs_freertos=freertos
 dirs_zxspectrum=zxspectrum
+dirs_msxdos=msxdos
 
 [install]
 fpcpackage=y

+ 226 - 0
rtl/msxdos/Makefile.fpc

@@ -0,0 +1,226 @@
+#
+#   Makefile.fpc for MSDOS RTL
+#
+[package]
+main=rtl
+[target]
+#loaders=prt0s prt0t prt0m prt0c prt0l prt0h # exceptn fpu
+units=system si_prc uuchar objpas iso7185
+#uuchar objpas strings dos heaptrc lnfodwrf sysconst sysutils \
+#      math macpas iso7185 extpas rtlconsts typinfo cpu types \
+#      getopts sortbase fgl classes \
+#      msmouse ports  \
+#      charset cpall ctypes \
+#      fpwidestring character unicodedata unicodenumtable
+#      cmem
+#      initc profile dxetype dxeload emu387 \
+#      cpu mmx \
+#      vesamode \
+# rsts=math typinfo classes dateutil sysconst
+implicitunits=exeinfo \
+      cp1250 cp1251 cp1252 cp1253 cp1254 cp1255 cp1256 cp1257 cp1258 \
+      cp437 cp646 cp737 cp775 cp850 cp852 cp855 cp856 cp857 cp860 cp861 cp862 \
+      cp863 cp864 cp865 cp866 cp869 cp874 cp3021 cp8859_1 cp8859_2 cp8859_3 cp8859_4 \
+      cp8859_5 cp8859_6 cp8859_7 cp8859_8 cp8859_9 cp8859_10 cp8859_11 \
+      cp8859_13 cp8859_14 cp8859_15 cp8859_16 cpkoi8_r cpkoi8_u
+
+[require]
+nortl=y
+[install]
+fpcpackage=y
+[default]
+fpcdir=../..
+target=msxdos
+cpu=z80
+[compiler]
+includedir=$(INC) $(PROCINC)
+sourcedir=$(INC) $(PROCINC) $(COMMON)
+[prerules]
+RTL=..
+INC=../inc
+COMMON=$(RTL)/common
+PROCINC=../$(CPU_TARGET)
+UNITPREFIX=rtl
+SYSTEMUNIT=system
+
+# Paths
+OBJPASDIR=$(RTL)/objpas
+# Insert exception handler in system unit
+ifdef EXCEPTIONS_IN_SYSTEM
+override FPCOPT+=-dEXCEPTIONS_IN_SYSTEM
+endif
+# Insert exception handler in system unit
+ifdef NO_EXCEPTIONS_IN_SYSTEM
+override FPCOPT+=-dNO_EXCEPTIONS_IN_SYSTEM
+endif
+[rules]
+# Get the system independent include file names.
+# This will set the following variables :
+# SYSINCNAMES
+include $(INC)/makefile.inc
+SYSINCDEPS=$(addprefix $(INC)/,$(SYSINCNAMES))
+# Get the processor dependent include file names.
+# This will set the following variables :
+# CPUINCNAMES
+include $(PROCINC)/makefile.cpu
+SYSCPUDEPS=$(addprefix $(PROCINC)/,$(CPUINCNAMES))
+# Put system unit dependencies together.
+SYSDEPS=$(SYSINCDEPS) $(SYSCPUDEPS)
+#
+# Loaders
+#
+ifneq ($(findstring -dTEST_I8086_SMARTLINK_SECTIONS,$(FPCOPT)),)
+override NASM_OPT+=-D__I8086_SMARTLINK_SECTIONS__
+endif
+
+#
+# System Units (System, Objpas, Strings)
+#
+system$(PPUEXT) : system.pp $(SYSDEPS) $(INC)/tnyheaph.inc $(INC)/tinyheap.inc registers.inc
+        $(COMPILER) $(FPC_SYSTEM_OPT) -Us -Sg system.pp
+	$(EXECPPAS)
+
+uuchar$(PPUEXT): $(SYSTEMUNIT)$(PPUEXT) $(INC)/uuchar.pp
+	$(COMPILER) $(INC)/uuchar.pp
+	$(EXECPPAS)
+
+objpas$(PPUEXT): $(OBJPASDIR)/objpas.pp $(INC)/except.inc system$(PPUEXT)
+        $(COMPILER) -Fi$(OBJPASDIR) $(OBJPASDIR)/objpas.pp
+	$(EXECPPAS)
+
+strings$(PPUEXT) : $(INC)/strings.pp $(INC)/stringsi.inc \
+                   $(PROCINC)/strings.inc $(PROCINC)/stringss.inc \
+                   $(INC)/genstr.inc $(INC)/genstrs.inc \
+                   system$(PPUEXT)
+	$(COMPILER) $(INC)/strings.pp
+	$(EXECPPAS)
+
+iso7185$(PPUEXT) : $(INC)/iso7185.pp system$(PPUEXT)
+        $(COMPILER) $(INC)/iso7185.pp
+	$(EXECPPAS)
+
+extpas$(PPUEXT) : $(INC)/extpas.pp dos$(PPUEXT) $(SYSTEMUNIT)$(PPUEXT)
+        $(COMPILER) $(INC)/extpas.pp
+        $(EXECPPAS)
+
+#
+# System Dependent Units
+#
+
+ports$(PPUEXT) : ports.pp system$(PPUEXT)
+	$(COMPILER) ports.pp
+	$(EXECPPAS)
+#
+# TP7 Compatible RTL Units
+#
+dos$(PPUEXT) : dos.pp registers.inc \
+               $(INC)/dosh.inc $(INC)/dos.inc $(INC)/fexpand.inc \
+               strings$(PPUEXT) system$(PPUEXT)
+	$(COMPILER) dos.pp
+	$(EXECPPAS)
+
+#
+# Delphi Compatible Units
+#
+sysutils$(PPUEXT) : sysutils.pp $(wildcard $(OBJPASDIR)/sysutils/*.inc) \
+                    objpas$(PPUEXT) dos$(PPUEXT) sysconst$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) -Fi$(OBJPASDIR)/sysutils sysutils.pp
+	$(EXECPPAS)
+
+classes$(PPUEXT) : classes.pp $(wildcard $(OBJPASDIR)/classes/*.inc) \
+                   sysutils$(PPUEXT) typinfo$(PPUEXT) rtlconsts$(PPUEXT) \
+                   types$(PPUEXT) fgl$(PPUEXT) sortbase$(PPUEXT) \
+                   objpas$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) -Fi$(OBJPASDIR)/classes classes.pp
+	$(EXECPPAS)
+
+fgl$(PPUEXT) : $(OBJPASDIR)/fgl.pp types$(PPUEXT) sysutils$(PPUEXT) rtlconsts$(PPUEXT) objpas$(PPUEXT) system$(PPUEXT) sortbase$(PPUEXT)
+        $(COMPILER) $(OBJPASDIR)/fgl.pp
+	$(EXECPPAS)
+
+math$(PPUEXT): $(OBJPASDIR)/math.pp $(PROCINC)/mathu.inc objpas$(PPUEXT) sysutils$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) $(OBJPASDIR)/math.pp
+	$(EXECPPAS)
+
+typinfo$(PPUEXT): $(OBJPASDIR)/typinfo.pp sysutils$(PPUEXT) objpas$(PPUEXT) rtlconsts$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) -Sg -Fi$(OBJPASDIR) $(OBJPASDIR)/typinfo.pp
+	$(EXECPPAS)
+
+types$(PPUEXT) : $(OBJPASDIR)/types.pp objpas$(PPUEXT) math$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) $(OBJPASDIR)/types.pp
+	$(EXECPPAS)
+
+rtlconsts$(PPUEXT) : $(OBJPASDIR)/rtlconsts.pp $(OBJPASDIR)/rtlconst.inc objpas$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) -Fi$(OBJPASDIR) $(OBJPASDIR)/rtlconsts.pp
+	$(EXECPPAS)
+
+sysconst$(PPUEXT) : $(OBJPASDIR)/sysconst.pp objpas$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) $(OBJPASDIR)/sysconst.pp
+	$(EXECPPAS)
+
+#
+# Mac Pascal Model
+#
+macpas$(PPUEXT) : $(INC)/macpas.pp objpas$(PPUEXT) math$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) $(INC)/macpas.pp $(REDIR)
+	$(EXECPPAS)
+
+#
+# Other system-independent RTL Units
+#
+cpu$(PPUEXT) : $(PROCINC)/cpu.pp sysutils$(PPUEXT) system$(PPUEXT)
+	$(COMPILER) $(PROCINC)/cpu.pp
+	$(EXECPPAS)
+
+getopts$(PPUEXT) : $(INC)/getopts.pp system$(PPUEXT)
+        $(COMPILER) $(INC)/getopts.pp $(REDIR)
+	$(EXECPPAS)
+
+heaptrc$(PPUEXT) : $(INC)/heaptrc.pp system$(PPUEXT)
+        $(COMPILER) -Sg $(INC)/heaptrc.pp
+	$(EXECPPAS)
+
+lnfodwrf$(PPUEXT) : $(INC)/lnfodwrf.pp exeinfo$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) $(INC)/lnfodwrf.pp
+	$(EXECPPAS)
+
+exeinfo$(PPUEXT) : $(INC)/exeinfo.pp strings$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) $(INC)/exeinfo.pp
+	$(EXECPPAS)
+
+charset$(PPUEXT) : $(INC)/charset.pp objpas$(PPUEXT) system$(PPUEXT)
+	$(COMPILER) $(INC)/charset.pp
+	$(EXECPPAS)
+
+cpall$(PPUEXT): $(RTL)/charmaps/cpall.pas system$(PPUEXT) charset$(PPUEXT)
+        $(COMPILER) -Fu$(INC) -Fi$(RTL)/charmaps $(RTL)/charmaps/cpall.pas
+	$(EXECPPAS)
+
+fpwidestring$(PPUEXT): $(OBJPASDIR)/fpwidestring.pp unicodedata$(PPUEXT) charset$(PPUEXT) objpas$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) $(OBJPASDIR)/fpwidestring.pp
+
+character$(PPUEXT): $(OBJPASDIR)/character.pas sysutils$(PPUEXT) objpas$(PPUEXT) rtlconsts$(PPUEXT) unicodedata$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) $(OBJPASDIR)/character.pas
+
+unicodenumtable$(PPUEXT) : $(OBJPASDIR)/unicodenumtable.pas objpas$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) -Fi$(OBJPASDIR) $(OBJPASDIR)/unicodenumtable.pas
+
+unicodedata$(PPUEXT) : $(OBJPASDIR)/unicodedata.pas unicodenumtable$(PPUEXT) objpas$(PPUEXT) system$(PPUEXT)
+        $(COMPILER) -Fi$(OBJPASDIR) $(OBJPASDIR)/unicodedata.pas
+
+sortbase$(PPUEXT) : $(INC)/sortbase.pp objpas$(PPUEXT) $(SYSTEMUNIT)$(PPUEXT)
+	$(COMPILER) $<
+
+#
+# Other system-dependent RTL Units
+#
+msmouse$(PPUEXT) : msmouse.pp system$(PPUEXT)
+        $(COMPILER) msmouse.pp $(REDIR)
+	$(EXECPPAS)
+
+ctypes$(PPUEXT) : $(INC)/ctypes.pp system$(PPUEXT)
+        $(COMPILER) $(INC)/ctypes.pp $(REDIR)
+	$(EXECPPAS)
+
+si_prc$(PPUEXT) : system$(PPUEXT)
+        $(COMPILER) si_prc.pp

+ 10 - 0
rtl/msxdos/registers.inc

@@ -0,0 +1,10 @@
+{ Registers record used by Intr and MsxDos. This include file is shared between
+  the system unit and the dos unit. }
+
+type
+  Registers = packed record
+    case Integer of
+      0: (BC, DE, AF, HL, IX, IY: Word);
+      1: (C, B, E, D, Flags, A, L, H, IXl, IXh, IYl, IYh: Byte);
+  end;
+

+ 24 - 0
rtl/msxdos/rtldefs.inc

@@ -0,0 +1,24 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2012 by Free Pascal development team
+
+    This file contains platform-specific defines that are used in
+    multiple RTL units.
+
+    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.
+
+ **********************************************************************}
+
+{ the single byte OS APIs always use UTF-8 }
+{ define FPCRTL_FILESYSTEM_UTF8}
+
+{ The OS supports a single byte file system operations API that we use }
+{$define FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
+
+{ The OS supports a two byte file system operations API that we use }
+{ define FPCRTL_FILESYSTEM_TWO_BYTE_API}

+ 36 - 0
rtl/msxdos/si_prc.pp

@@ -0,0 +1,36 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2020 by Free Pascal development team
+
+    This file contains startup code for the ZX Spectrum
+
+    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 si_prc;
+
+{$SMARTLINK OFF}
+
+interface
+
+implementation
+
+var
+  stktop: word; external name '__stktop';
+
+procedure PascalMain; external name 'PASCALMAIN';
+
+{ this *must* always remain the first procedure with code in this unit }
+procedure _start; assembler; nostackframe; public name 'start';
+asm
+    ld (stktop), sp
+    jp PASCALMAIN
+end;
+
+end.

+ 153 - 0
rtl/msxdos/sysdir.inc

@@ -0,0 +1,153 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 1999-2000 by Florian Klaempfl and Pavel Ozerski
+    member of the Free Pascal development team.
+
+    FPC Pascal system unit for the Win32 API.
+
+    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.
+
+ **********************************************************************}
+
+{*****************************************************************************
+                           Directory Handling
+*****************************************************************************}
+
+{$ifdef todo}
+procedure DosDir(func:byte;s: rawbytestring);
+var
+  regs   : Registers;
+  len    : Integer;
+begin
+  DoDirSeparators(s);
+  { True DOS does not like backslashes at end
+    Win95 DOS accepts this !!
+    but "\" and "c:\" should still be kept and accepted hopefully PM }
+  len:=length(s);
+  if (len>0) and (s[len]='\') and
+     Not ((len=1) or ((len=3) and (s[2]=':'))) then
+    s[len]:=#0;
+  regs.DX:=Ofs(s[1]);
+  regs.DS:=Seg(s[1]);
+  if LFNSupport then
+   regs.AX:=$7100+func
+  else
+   regs.AX:=func shl 8;
+  MsDos(regs);
+  if (regs.Flags and fCarry) <> 0 then
+   GetInOutRes(regs.AX);
+end;
+{$endif}
+
+Procedure do_MkDir(const s: rawbytestring);
+begin
+   GetInOutRes(153);
+   //DosDir($39,s);
+end;
+
+Procedure do_RmDir(const s: rawbytestring);
+begin
+  if s='.' then
+    begin
+      InOutRes:=16;
+      exit;
+    end;
+  GetInOutRes(153);
+  //DosDir($3a,s);
+end;
+
+Procedure do_ChDir(const s: rawbytestring);
+{$ifdef todo}
+var
+  regs : Registers;
+  len  : Integer;
+{$endif}
+begin
+  GetInOutRes(153);
+{$ifdef todo}
+  len:=Length(s);
+{ First handle Drive changes }
+  if (len>=2) and (s[2]=':') then
+   begin
+     regs.DX:=(ord(s[1]) and (not 32))-ord('A');
+     regs.AX:=$0e00;
+     MsDos(regs);
+     regs.AX:=$1900;
+     MsDos(regs);
+     if regs.AL<>regs.DL then
+      begin
+        Inoutres:=15;
+        exit;
+      end;
+     { DosDir($3b,'c:') give Path not found error on
+       pure DOS PM }
+     if len=2 then
+       exit;
+   end;
+{ do the normal dos chdir }
+  DosDir($3b,s);
+{$endif}
+end;
+
+procedure do_GetDir (DriveNr: byte; var Dir: RawByteString);
+{$ifdef todo}
+var
+  temp : array[0..260] of char;
+  i    : integer;
+  regs : Registers;
+{$endif}
+begin
+  GetInOutRes(153);
+{$ifdef todo}
+  regs.DX:=drivenr;
+  regs.SI:=Ofs(temp);
+  regs.DS:=Seg(temp);
+  if LFNSupport then
+   regs.AX:=$7147
+  else
+   regs.AX:=$4700;
+  MsDos(regs);
+  if (regs.Flags and fCarry) <> 0 then
+   Begin
+     GetInOutRes (regs.AX);
+     Dir := char (DriveNr + 64) + ':\';
+     SetCodePage (Dir,DefaultFileSystemCodePage,false);
+     exit;
+   end
+  else
+    temp[252] := #0;  { to avoid shortstring buffer overflow }
+{ conversion to Pascal string including slash conversion }
+  i:=0;
+  SetLength(dir,260);
+  while (temp[i]<>#0) do
+   begin
+     if temp[i] in AllowDirectorySeparators then
+       temp[i]:=DirectorySeparator;
+     dir[i+4]:=temp[i];
+     inc(i);
+   end;
+  dir[2]:=':';
+  dir[3]:='\';
+  SetLength(dir,i+3);
+  SetCodePage (dir,DefaultFileSystemCodePage,false);
+{ upcase the string }
+  if not FileNameCasePreserving then
+   dir:=upcase(dir);
+  if drivenr<>0 then   { Drive was supplied. We know it }
+   dir[1]:=char(65+drivenr-1)
+  else
+   begin
+   { We need to get the current drive from DOS function 19H  }
+   { because the drive was the default, which can be unknown }
+     regs.AX:=$1900;
+     MsDos(regs);
+     i:= (regs.AX and $ff) + ord('A');
+     dir[1]:=chr(i);
+   end;
+{$endif}
+end;

+ 373 - 0
rtl/msxdos/sysfile.inc

@@ -0,0 +1,373 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2001 by Free Pascal development team
+
+    Low leve file functions
+
+    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.
+
+ **********************************************************************}
+
+   { Keep Track of open files }
+   const
+      max_files = 50;
+   var
+      openfiles : array [0..max_files-1] of boolean;
+{$ifdef SYSTEMDEBUG}
+      opennames : array [0..max_files-1] of pchar;
+   const
+      free_closed_names : boolean = true;
+      verbose_files : boolean = true;
+
+{$endif SYSTEMDEBUG}
+
+
+{****************************************************************************
+                        Low level File Routines
+ ****************************************************************************}
+
+procedure do_close(handle : thandle);
+var
+  regs: Registers;
+begin
+  if Handle <= 4 then
+   exit;
+  regs.A := 0;
+  regs.B := Byte(handle);
+  if handle < max_files then
+    begin
+{$ifdef SYSTEMDEBUG}
+       if not openfiles[handle] then
+          Writeln(stderr,'Trying to close file h=',handle,' marked as closed');
+       if assigned(opennames[handle]) and free_closed_names then
+         begin
+            if verbose_files then
+              Writeln(stderr,'file ',opennames[handle],' closed');
+
+            sysfreememsize(opennames[handle],strlen(opennames[handle])+1);
+            opennames[handle]:=nil;
+         end;
+{$endif SYSTEMDEBUG}
+       openfiles[handle]:=false;
+    end;
+  regs.C := $45;
+  MsxDos(regs);
+  if regs.A <> 0 then
+   begin
+     GetInOutRes(regs.A);
+{$ifdef SYSTEMDEBUG}
+     if verbose_files then
+       Writeln(stderr,'file close failed A = ',regs.A);
+{$endif SYSTEMDEBUG}
+   end;
+end;
+
+
+procedure do_erase(p : pchar; pchangeable: boolean);
+var
+  regs: Registers;
+  oldp: pchar;
+begin
+  oldp := p;
+  DoDirSeparators(p, pchangeable);
+  regs.A := 0;
+  regs.C := $4D;
+  regs.DE := PtrUInt(p);
+  MsxDos(regs);
+  if regs.A <> 0 then
+   GetInOutRes(regs.A);
+  if p <> oldp then
+    freemem(p);
+end;
+
+
+procedure do_rename(p1,p2 : pchar; p1changeable, p2changeable: boolean);
+var
+  regs: Registers;
+  oldp1, oldp2: pchar;
+begin
+  oldp1 := p1;
+  oldp2 := p2;
+  DoDirSeparators(p1, p1changeable);
+  DoDirSeparators(p2, p2changeable);
+  regs.A := 0;
+  regs.C := $4E;
+  { ToDo: check for same directory? }
+  regs.DE := PtrUInt(p1);
+  regs.HL := PtrUInt(p2);
+  MsxDos(regs);
+  if regs.A <> 0 then
+   GetInOutRes(regs.A);
+  if p1 <> oldp1 then
+    freemem(p1);
+  if p2 <> oldp2 then
+    freemem(p2);
+end;
+
+
+function do_write(h:thandle;addr:pointer;len : longint) : longint;
+var
+  regs: Registers;
+begin
+  regs.C := $49;
+  regs.A := 0;
+  regs.B := h;
+  regs.DE := PtrUInt(addr);
+  regs.HL := len;
+  MsxDos(regs);
+  if regs.A <> 0 then
+  begin
+    GetInOutRes(regs.A);
+    exit(0);
+  end;
+  do_write := regs.HL;
+end;
+
+
+function do_read(h:thandle;addr:pointer;len : longint) : longint;
+var
+  regs: Registers;
+begin
+  regs.C := $48;
+  regs.A := 0;
+  regs.B := h;
+  regs.DE := PtrUInt(addr);
+  regs.HL := len;
+  MsxDos(regs);
+  if regs.A <> 0 then
+  begin
+    GetInOutRes(regs.A);
+    exit(0);
+  end;
+  do_read := regs.HL;
+end;
+
+
+function do_filepos(handle : thandle) : longint;
+var
+  regs : Registers;
+begin
+  regs.C := $4A;
+  regs.B := handle;
+  regs.A := 1;
+  regs.DE := 0;
+  regs.HL := 0;
+  MsxDos(regs);
+  if regs.A <> 0 then
+    begin
+      GetInOutRes(regs.A);
+      do_filepos := 0;
+    end
+  else
+    do_filepos := (longint(regs.DE) shl 16) + regs.HL;
+end;
+
+
+procedure do_seek(handle:thandle;pos : longint);
+var
+  regs: Registers;
+begin
+  regs.C := $4A;
+  regs.B := handle;
+  regs.A := 0;
+  regs.DE := pos shr 16;
+  regs.HL := pos and $ffff;
+  MsxDos(regs);
+  if regs.A <> 0 then
+    GetInOutRes(regs.A);
+end;
+
+
+
+function do_seekend(handle:thandle):longint;
+var
+  regs : Registers;
+begin
+  regs.C := $4A;
+  regs.B := handle;
+  regs.A := 2;
+  regs.DE := 0;
+  regs.HL := 0;
+  MsxDos(regs);
+  if regs.A <> 0 then
+    begin
+      GetInOutRes(regs.A);
+      do_seekend := 0;
+    end
+  else
+    do_seekend := (longint(regs.DE) shl 16) + regs.HL;
+end;
+
+
+function do_filesize(handle : thandle) : longint;
+var
+  aktfilepos : longint;
+begin
+  aktfilepos:=do_filepos(handle);
+  do_filesize:=do_seekend(handle);
+  do_seek(handle,aktfilepos);
+end;
+
+
+{ truncate at a given position }
+procedure do_truncate (handle:thandle;pos:longint);
+{var
+  regs : Registers;}
+begin
+  GetInOutRes(153);
+  {do_seek(handle,pos);
+  regs.C:=??;
+  regs.B:=handle;
+  MsxDos(regs);
+  if regs.A <> 0 then
+   GetInOutRes(regs.A);}
+end;
+
+procedure do_open(var f;p:pchar;flags:longint; pchangeable: boolean);
+{
+  filerec and textrec have both handle and mode as the first items so
+  they could use the same routine for opening/creating.
+  when (flags and $100)   the file will be append
+  when (flags and $1000)  the file will be truncate/rewritten
+  when (flags and $10000) there is no check for close (needed for textfiles)
+}
+var
+  regs   : Registers;
+  action : word;
+  oldp : pchar;
+begin
+{$ifdef SYSTEMDEBUG}
+  if verbose_files then
+     Writeln(stderr,'do_open for file "',p,'" called');
+{$endif SYSTEMDEBUG}
+{ close first if opened }
+  if ((flags and $10000)=0) then
+   begin
+     case filerec(f).mode of
+      fminput,fmoutput,fminout : Do_Close(filerec(f).handle);
+      fmclosed : ;
+     else
+      begin
+        inoutres:=102; {not assigned}
+        exit;
+      end;
+     end;
+   end;
+{ reset file handle }
+  filerec(f).handle:=UnusedHandle;
+  action:=$1;
+{ convert filemode to filerec modes }
+  regs.A:=0;
+  case (flags and 3) of
+   0 : begin
+     filerec(f).mode:=fminput;
+     { b1 -> no write }
+     regs.A := 1;
+   end;
+   1 : begin
+     filerec(f).mode:=fmoutput;
+     { b2 -> no read }
+     regs.A := 2;
+   end;
+   2 : filerec(f).mode:=fminout;
+  end;
+  if (flags and $1000)<>0 then
+   action:=$12; {create file function}
+{ empty name is special }
+  if p[0]=#0 then
+   begin
+     case FileRec(f).mode of
+       fminput :
+         FileRec(f).Handle:=StdInputHandle;
+       fminout, { this is set by rewrite }
+       fmoutput :
+         FileRec(f).Handle:=StdOutputHandle;
+       fmappend :
+         begin
+           FileRec(f).Handle:=StdOutputHandle;
+           FileRec(f).mode:=fmoutput; {fool fmappend}
+         end;
+     end;
+     exit;
+   end;
+  oldp:=p;
+  DoDirSeparators(p,pchangeable);
+
+  if (action and $00f0) <> 0 then
+    regs.C := $44                     { Map to Create/Replace API }
+  else
+    regs.C := $43;                    { Map to Open_Existing API }
+
+  regs.B := 0;
+
+  MsxDos(regs);
+
+  if regs.A <> 0 then
+    begin
+      FileRec(f).mode:=fmclosed;
+      GetInOutRes(regs.A);
+      if oldp<>p then
+        freemem(p);
+{$ifdef SYSTEMDEBUG}
+      if verbose_files then
+        Writeln(stderr,'MSXDOS INT open for file "',p,'" failed err=',regs.A);
+{$endif SYSTEMDEBUG}
+      exit;
+    end
+  else
+    begin
+      filerec(f).handle:=regs.B;
+    end;
+{$ifdef SYSTEMDEBUG}
+  if verbose_files then
+     Writeln(stderr,'MSXDOS INT open for file "',p,'" returned ',regs.B);
+{$endif SYSTEMDEBUG}
+  if regs.B<max_files then
+    begin
+{$ifdef SYSTEMDEBUG}
+       if openfiles[regs.B] and
+          assigned(opennames[regs.B]) then
+         begin
+            Writeln(stderr,'file ',opennames[regs.B],'(',regs.B,') not closed but handle reused!');
+            sysfreememsize(opennames[regs.B],strlen(opennames[regs.B])+1);
+         end;
+{$endif SYSTEMDEBUG}
+       openfiles[regs.B]:=true;
+{$ifdef SYSTEMDEBUG}
+       opennames[regs.B] := sysgetmem(strlen(p)+1);
+       move(p^,opennames[regs.B]^,strlen(p)+1);
+       if verbose_files then
+         Writeln(stderr,'file ',opennames[regs.B],' opened');
+{$endif SYSTEMDEBUG}
+    end;
+{ append mode }
+  if ((flags and $100) <> 0) and
+   (FileRec (F).Handle <> UnusedHandle) then
+   begin
+     do_seekend(filerec(f).handle);
+     filerec(f).mode:=fmoutput; {fool fmappend}
+   end;
+
+  if oldp<>p then
+    freemem(p);
+end;
+
+
+function do_isdevice(handle:THandle):boolean;
+var
+  regs: Registers;
+begin
+  regs.C := $4B;
+  regs.B := handle;
+  regs.A := $00;
+  MsxDos(regs);
+  do_isdevice := (regs.D and $80) <> 0;
+  if regs.A <> 0 then
+   GetInOutRes(regs.A);
+end;
+

+ 117 - 0
rtl/msxdos/sysheap.inc

@@ -0,0 +1,117 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2001 by Free Pascal development team
+
+    This file implements all the base types and limits required
+    for a minimal POSIX compliant subset required to port the compiler
+    to a new OS.
+
+    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.
+
+ **********************************************************************}
+
+
+{*****************************************************************************
+                              Heap Management
+*****************************************************************************}
+
+{$ifdef DEBUG_TINY_HEAP}
+{ Internal structure used by MSDOS }
+type
+  MCB = packed record
+    sig : char;
+    psp : word;
+    paragraphs : word;
+    res : array [0..2] of char;
+    exename : array [0..7] of char;
+  end;
+  PMCB = ^MCB;
+{$endif def DEBUG_TINY_HEAP}
+
+
+function SysOSAlloc (size: ptruint): pointer;
+var
+  regs : Registers;
+  nb_para : longint;
+{$ifdef DEBUG_TINY_HEAP}
+  p : pmcb;
+  i : byte;
+{$endif def DEBUG_TINY_HEAP}
+begin
+{$ifdef DEBUG_TINY_HEAP}
+  writeln('SysOSAlloc called size=',size);
+{$endif}
+{$if defined(FPC_X86_DATA_FAR) or defined(FPC_X86_DATA_HUGE)}
+  regs.ax:=$4800;
+  nb_para:=size div 16;
+  if nb_para > $ffff then
+    begin
+{$ifdef DEBUG_TINY_HEAP}
+      writeln('SysOSAlloc size too big = ',size);
+{$endif}
+      result:=nil;
+    end
+  else
+    begin
+      regs.bx:=nb_para;
+      msdos(regs);
+      if (regs.Flags and fCarry) <> 0 then
+        begin
+{$ifdef DEBUG_TINY_HEAP}
+          writeln('SysOSAlloc failed, err = ',regs.AX);
+{$endif}
+          { Do not set InOutRes if ReturnNilIfGrowHeapFails is set }
+          if not ReturnNilIfGrowHeapFails then
+            GetInOutRes(regs.AX);
+          Result := nil;
+        end
+      else
+        begin
+          result:=ptr(regs.ax,0);
+{$ifdef DEBUG_TINY_HEAP}
+          writeln('SysOSAlloc returned= $',hexstr(regs.ax,4),':$0');
+          p:=ptr(regs.ax-1,0);
+          writeln('Possibly prev MCB: at ',hexstr(p));
+          writeln('  sig=',p^.sig);
+          writeln('  psp=$',hexstr(p^.psp,4));
+          writeln('  paragraphs=',p^.paragraphs);
+          if (p^.exename[0]<>#0) then
+            begin
+              write('  name=');
+              for i:=0 to 7 do
+                if ord(p^.exename[i])>31 then
+                  write(p^.exename[i]);
+              writeln;
+            end;
+          p:=ptr(regs.ax+p^.paragraphs,0);
+          writeln('Possibly next MCB: at ',hexstr(p));
+          writeln('  sig=',p^.sig);
+          writeln('  psp=$',hexstr(p^.psp,4));
+          writeln('  paragraphs=',p^.paragraphs);
+          if (p^.exename[0]<>#0) then
+            begin
+              write('  name=');
+              for i:=0 to 7 do
+                if ord(p^.exename[i])>31 then
+                  write(p^.exename[i]);
+              writeln;
+            end;
+{$endif}
+        end;
+    end;
+{$else not DATA_FAR}
+{$ifdef DEBUG_TINY_HEAP}
+    writeln('SysOSAlloc cannot be used in small data models');
+{$endif}
+  Result := nil;
+{$endif not DATA_FAR}
+end;
+
+procedure SysOSFree(p: pointer; size: ptruint);
+begin
+end;

+ 33 - 0
rtl/msxdos/sysos.inc

@@ -0,0 +1,33 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2013 by Free Pascal development team
+
+    This file implements all the base types and limits required
+    for a minimal POSIX compliant subset required to port the compiler
+    to a new OS.
+
+    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.
+
+ **********************************************************************}
+
+procedure GetInOutRes(def: Word);
+var
+  regs : Registers;
+begin
+  regs.C:=$65;
+  MsxDos(regs);
+  InOutRes:=regs.B;
+  case InOutRes of
+   19 : InOutRes:=150;
+   21 : InOutRes:=152;
+   32 : InOutRes:=5;
+  end;
+  if InOutRes=0 then
+    InOutRes:=Def;
+end;
+

+ 28 - 0
rtl/msxdos/sysosh.inc

@@ -0,0 +1,28 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2013 by Free Pascal development team
+
+    This file implements all the base types and limits required
+    for a minimal POSIX compliant subset required to port the compiler
+    to a new OS.
+
+    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.
+
+ **********************************************************************}
+
+{Platform specific information}
+type
+  THandle = Byte;
+  TThreadID = THandle;
+  TOSTimestamp = Longint;
+
+  PRTLCriticalSection = ^TRTLCriticalSection;
+  TRTLCriticalSection = record
+    Locked: boolean
+  end;
+

+ 722 - 0
rtl/msxdos/system.pp

@@ -0,0 +1,722 @@
+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.