---------------------------------------------------------------------------
D E L P H I  D O E S  D O S
---------------------------------------------------------------------------
Revisions
  Mar 1995: Original document created
  Apr 1995: Corrections (-ERRC); Additions (SYSUTILS, CLASSES)
  Sep 1995: Updates, Additions (TYPINFO, DELPHIPM.EXE, Debugging)
---------------------------------------------------------------------------

Outline
  I.    TURBO.TPL, DOS real mode system library
  II.   TPP.TPL, DOS protected mode system library
  III.  SYSUTILS, getting it to compile
  IV.   CLASSES, getting it to compile
  V.    TYPINFO, converting TYPINFO.DCU to TYPINFO.TPP
  VI.   DELPHIPM, Delphi IDE creates DOS protected mode programs
  VII.  Debugging, Turbo Debugger 4.6 w/TDXDEBUG.DLL
  VIII. Notes
  IX.   Disclaimer
  
---------------------------------------------------------------------------
Instructions for preparing DELPHI to compile a DOS EXE with DCC.EXE

Requirements:
  1) BP7 Runtime library source
  2) Delphi VCL source
  3) TASM.EXE
  4) The new DLIB.EXE (15616 bytes)
     (available from CompuServe DELPHI forum)
  5) Common sense

Assumptions:
  1) BP7 RTL is in \BP\RTL
  2) Delphi VCL is in \DELPHI\SOURCE
  3) \DELPHI\BIN is in your path as well as TASM.EXE
  4) You can at least read ASM code

Copy from \BP\RTL\SYS to \DELPHI\SOURCE\RTL\SYS (all are .ASM):
  MAIN, PARS, LAST, HEAP, F87H, EI87, EI86, DLIB, DAPP
  (* note ERRC that was in the original list has been removed - it should
     not be copied over or exception handling will not work !!!!! *)


---------------------------------------------------------------------------
I. DOS Real Mode ----------------------------------------------------------
---------------------------------------------------------------------------
  Objective: create a TURBO.TPL with units compiled under DELPHI

1) The first and main requirement is to get a SYSTEM.TPU for DOS mode.
This will take some work because Delphi VCL source does not include
MAIN.ASM which is required for compiling SYSTEM.PAS in DOS mode.  
Some things need to be removed from MAIN.ASM because they now exist 
in EXIT.ASM:
  - remove HaltTurbo, HaltError, Terminate, PrintString from the PUBLIC
    section.
  - remove the procedure code for the above procedures.  (HaltError
    starts in around line 210 or so and HaltTurbo starts around 230
    or so.  PrintString ends around 395 +/- a few.)
  - up at the top in Externals after "ASSUME  CS:CODE,DS:DATA" add 
    another "EXTRN HaltError:NEAR,HaltTurbo:NEAR"
  - down in "Int3FHandler" remove "SHORT" from "JMP SHORT HaltError"
  - after "MOV AX,200" in Int00Handler add "JMP HaltError"
  - after "MOV AX,255" in Int23Handler add "JMP HaltTurbo"

2) Create OBJ from ASM: In \DELPHI\SOURCE\RTL\SYS type 
     TASM *.ASM

3) Compile SYSTEM: In \DELPHI\SOURCE\RTL\SYS type
     DCC -cd -$d- -o\BP\RTL\LIB SYSTEM
     
4) Start your TURBO.TPL: In \DELPHI\BIN type
     DLIB TURBO.TPL +..\SOURCE\RTL\SYS\SYSTEM.TPU
    
Other files you may want to compile and include in TURBO.TPL:
  unit      source can be found
  OVERLAY   \BP\RTL\OVR
  CRT       \BP\RTL\CRT
  DOS       \BP\RTL\DOS (need to use TASM again)
  PRINTER   \BP\RTL\PRT
  STRINGS   \DELPHI\SOURCE\RTL70    (optional)
  MEMORY    \BP\RTL\TV              (optional)
  OBJECTS   \BP\RTL\COMMON          (optional)
Compile the above files in the specified directory using DCC with the
parameters -cd and -$d- to create "ver 8.0" .TPU files.  Then add the
TPU files to TURBO.TPL as shown in step 4.

Now you're all set!  Use DCC with the "undocumented" -cd switch to create
DOS EXEs with classes, exception handling, etc.

Note: Run-Time errors are converted to exceptions only if you include
      SysUtils in you program.  SysUtils does not compile in DOS real
      mode.  See section III. for info on compiling SysUtils in DPMI.


---------------------------------------------------------------------------
II. DOS Protected Mode (DPMI) ---------------------------------------------
---------------------------------------------------------------------------
  Objective: create a TPP.TPL with units compiled under DELPHI

1) Complete step 1 above.  Make sure the .OBJ files still exist 
   from step 2 above.

2) Create OBP from ASM: In \DELPHI\SOURCE\RTL\SYS type 
     TASM -op -d_DPMI_ *.ASM *.OBP

3) Compile SYSTEM: In \DELPHI\SOURCE\RTL\SYS type
     DCC -cp -$d- -o\BP\RTL\LIB SYSTEM
     
4) Start your TURBO.TPL: In \DELPHI\BIN type
     DLIB TPP.TPL +..\SOURCE\RTL\SYS\SYSTEM.TPP
    
Other files you may want to compile and include in TPP.TPL:
  unit      source can be found
  CRT       \BP\RTL\CRT
  DOS       \BP\RTL\DOS (TASM -op -d_DPMI_ *.ASM *.OBP)
  PRINTER   \BP\RTL\PRT
  STRINGS   \DELPHI\SOURCE\RTL70    
  WINDOS    \DELPHI\SOURCE\RTL70
  WINAPI    \DELPHI\SOURCE\RTL\WIN
  MEMORY    \BP\RTL\TV              (optional)
  OBJECTS   \BP\RTL\COMMON          (optional)
  SYSUTILS  \DELPHI\SOURCE\RTL\SYS  (new, see below)
  CLASSES   \DELPHI\SOURCE\VCL
Compile the above files in the specified directory using DCC with the
parameters -cp and -$d- to create "ver 8.0" .TPP files.  Then add the
TPP files to TPP.TPL as shown in step 4.


---------------------------------------------------------------------------
III. SysUtils for DOS Protected Mode --------------------------------------
---------------------------------------------------------------------------

*****
***** I suggest creating the directory \DELPHI\SOURCE\DOS for all the
***** following work on SYSUTILS, TYPINFO, CLASSES, etc.
*****
     
To get SYSUTILS to work properly in DOS PM you need to make a few minor
changes to the source file (comes with VCL Source).  First make a backup
of the SYSUTILS.PAS file and then I suggest puting "{$IFDEF WINDOWS}..." 
around all of your changes.

1) FileRead/FileWrite: RTM.EXE only supports _lread/write not _hread/write
   and SysUtils needs to be updated to reflect that.  I will use FileRead
   as the example:
   In INTERFACE
     {$IFDEF WINDOWS}
     function FileRead(Handle: Integer; var Buffer; Count: Longint): Longint;
     {$ELSE}
     function FileRead(Handle: Integer; var Buffer; Count: Word): Word;
     {$ENDIF}
   In IMPLEMENTATION
     function FileRead(Handle: Integer; var Buffer; Count: Word): Word;
       external 'KERNEL' index 82; { _lread }
   FileWrite needs the same changes and it has an external index of 86.
   
2) Units used in the IMPLEMENTATION section:
     {$IFDEF WINDOWS}
     uses WinTypes, WinProcs, ToolHelp;
     {$ELSE}
     uses WinAPI;
     {$ENDIF}

3) Remove some exception (hardware, etc) handling.  Unfortunately RTM
   does not support MakeProcInstance and FreeProcInstance otherwise 
   none of the following would have to be removed.
   A) in the INTERFACE section
      {$IFDEF WINDOWS}
      procedure EnableExceptionHandler(Enable: Boolean);
      {$ENDIF}
      
   B) Around the procedure "GetModNameAndLogAddr" in the IMPLEMENTATION
      section add {$IFDEF WINDOWS} and {$ENDIF}.
      
   C) In the procedure "ShowException":
      var
        .. existing definitions
        Buffer: array[0..255] of Char;
        {$IFDEF WINDOWS}
        GlobalEntry: TGlobalEntry;
        hMod: THandle;
        {$ENDIF}
      begin
        {$IFDEF WINDOWS}
        .. existing code
        {$ENDIF}
      end;
   D) Before the procedure "ErrorHandler":
      {$IFDEF WINDOWS}
      const
        Flags   = $10;
        .. other consts
            
        Recurse: Word = 0;
      {$ENDIF}
   E) In the procedure "ErrorHandler":
        1: E := OutOfMemory;
        {$IFDEF WINDOWS}
        2,4..10: with ExceptMap[ErrorCode] do E := EClass.CreateRes(EIdent);
        3,11..16:
        ..
        end;
        {$ELSE}
        2..16: with ExceptMap[ErrorCode] do E := EClass.CreateRes(EIdent);
        {$ENDIF}
      else
   F) Before the procedure "InterruptCallBack" add {$IFDEF WINDOWS} and
      after the end of the procedure "EnableExceptionHandler" add {$ENDIF}.
   G) In procedure "DoneExceptions" put IFDEF WINDOWS around the call to
      EnableExceptionHandler.
   H) In procedure "InitExceptions" put IFDEF WINDOWS around the assignment
      "TaskID := GetCurrentTask;" and the call to EnableExceptionHandler.
      
4) Add profile support provided in RTM.EXE.  After the comment 
   "{ Initialization file support }" add the following:
      {$IFNDEF WINDOWS}
      function GetProfileInt( appName, keyName : pchar;
        default : integer ) : word; far; external 'KERNEL' index 57;
      function GetProfileString( appName, keyName, default, returned : pchar;
        size : integer ) : integer; far; external 'KERNEL' index 58; 
      {$ENDIF}                                                       
                                                                    
Compile SYSUTILS for DOS PM from the command line:
  DCC -cp -$d- -r\DELPHI\LIB SYSUTILS
  
It is all set to add the TPP.TPL and use in your programs.  I have not 
been able to test every function that is provided.  It would be diffucult
to get SysUtils working for DOS Real Mode because of its use of resource
files, although I am sure it is possible if someone would like to give it 
a shot.  You could probably remove all the exception handling and get it
to compile with BP7.


---------------------------------------------------------------------------
IV. Classes for DOS Protected Mode ----------------------------------------
---------------------------------------------------------------------------

Getting the CLASSES unit to compile for DOS PM is very simple compared to
SYSUTILS.  

In the INTERFACE section change the uses clause:
  {$IFDEF WINDOWS}
  uses SysUtils, WinTypes, WinProcs;
  {$ELSE}
  uses SysUtils, WinAPI;
  {$ENDIF}

You need to create a unit called CONSTS that has the following:
(This information was obtained via the online browser because
Borland does not proved CONSTS.PAS - don't ask me why not):
========================================
unit Consts;

{$R CONSTS.RES}

interface

const
  SAssignError               = 61440;
  SFCreateError              = 61441;
  SFOpenError                = 61442;
  SReadError                 = 61443;
  SWriteError                = 61444;
  SMemoryStreamError         = 61445;
  SClassNotFound             = 61447;
  SInvalidImage              = 61448;
  SResNotFound               = 61449;
  SListIndexError            = 61451;
  SSortedListError           = 61452;
  SDuplicateString           = 61453;
  SDuplicateName             = 61455;
  SInvalidName               = 61456;
  SDuplicateClass            = 61457;
  SLineTooLong               = 61459;
  SInvalidPropertyValue      = 61460;
  SInvalidPropertyPath       = 61461;
  SUnknownProperty           = 61462;
  SReadOnlyProperty          = 61463;
  SPropertyException         = 61464;
  SRegisterError             = 61498;
  SParseError                = 61530;
  SIdentifierExpected        = 61531;
  SStringExpected            = 61532;
  SNumberExpected            = 61533;
  SCharExpected              = 61534;
  SSymbolExpected            = 61535;
  SInvalidString             = 61537;
  SInvalidProperty           = 61538;
  SInvalidBinary             = 61539;

implementation

end.
========================================

Now copy TYPINFO.INT to TYPINFO.PAS and copy the procedure/function 
declarations from the INTERFACE to IMPLEMENTATION.  With each procedure and
function add a "Begin End;".  Have each function return NIL, 0, etc 
depending on its type.

Compile:
  DCC -cp -$d- -r\DELPHI\LIB CLASSES
  
You can now use TList, TStrings, TStringList, TStream, etc in your DOS PM 
programs.  Because of changes to SysUtils THandleStream.Read/Write is
limited to a buffer size of word and not longint.  You won't be able to 
use TReader and TWriter unless you can get Borland to give you the source 
or TPP for TYPINFO.PAS (or follow step V. below).


---------------------------------------------------------------------------
V. TypInfo for DOS Protected Mode -----------------------------------------
---------------------------------------------------------------------------

With a little work you can convert the TYPINFO.DCU binary into a 
TYPINFO.TPP file for Delphi.
  - You must change a flag in the header indicating it is a DPMI unit.
  - You must change the ID stamps after each uses unit to match their
    corresponding unit's IDs.
The reason you would want this is to enable object persistance.  This 
will allow you to save objects that have published properties to a 
stream (and load from stream).  See TStream.ReadComponent/WriteComponent.
    
Note: I found this information out accidentally when I noticed that 
      the .DCU and .TPP files for the same source unit were the same 
      size.  (The source units contained no IFDEFs based on compile 
      mode.)  I did a binary file compare on them and the only difference 
      was the byte at offset 2F (12 for DCU and 11 for TPP).

Assumptions: 
  - TYPINFO.DCU 5936 02/15/95 08:00:00am
    
1) Take the TYPINFO.TPP that was created in step IV (Classes) and rename
   it to TYPINFO.OLD.
   
2) TDUMP TYPINFO.OLD to a file and print the first page.

3) Copy TYPINFO.DCU from \DELPHI\LIB to the current directory (I assume
   \DELPHI\SOURCE\DOS) as TYPINFO.TPP.
   
4) Load TYPINFO.TPP (the new one) into a hex file editor (NU's DISKEDIT
   for example).
   
5) Change the byte at offset 2F from 12 (Windows) to 11 (DPMI).

6) Here is the tricky part.  
   - Notice the table of units that starts around offset E5.  You
     should see TypInfo, System, Classes, and SysUtils.
   - Each unit name is followed by two null bytes and then two bytes
     that make up some sort of ID.
   - In the .TPP file that is a copy of the .DCU file these ID bytes
     should probably be as follows: TypInfo, 60 44; System, 34 0E;
     Classes, F5 D8; and SysUtils, 40 FC.
   - Now compare those to the bytes in the same positions in the 
     TYPINFO.OLD file dump that you printed.
   - What you need to do is modify the bytes in the .TPP file that
     you have open and make them the same as the bytes in the .OLD
     file that you compiled.
     - I do not know if this is true in all cases but the new numbers
       on my system are as follows: TypInfo, DE 65; System, 49 12;
       Classes, 82 ED; and SysUtils, FA B5.  The number might be
       different in your case (?).
       
   (you should probably rename your TYPINFO.PAS now so that it doesn't
    end up getting recompiled in the future)

7) Recompile CLASSES.PAS

Note: I have not done much testing in this area.  In theory if you have
      TypInfo then you should be able to have persistent classes (with
      some work) using TReader and TWriter, etc.  "Published" properties 
      are what is stored (as well as properties defined in the
      "DefineProperties" method) when using persistent classes (anything 
      descendent from TPersistent, such as TStrings & TComponent).


---------------------------------------------------------------------------
VI. Creating an IDE that generates TPP units and DOS DPMI EXEs!!!!!!!!!!!!!
---------------------------------------------------------------------------

After you have your TPP.TPL in the DELPHI\BIN directory you can actually 
get the Delphi IDE to recognize it and create DOS programs.  You have to 
do it with a separate copy of DELPHI.EXE because it needs modification.

Assumptions: 
  - DELPHI.EXE 1188864 04/25/95 08:00:00am
  - You have successfully created TPP.TPL

1) Copy DELPHI.EXE to DELPHIPM.EXE.  When you run DELPHIPM.EXE it will
   generate its own DELPHIPM.INI file in the Windows directory so you
   don't have to worry about the changes (such as eliminating COMPLIB.DCL)
   you make affecting the normal Delphi Windows IDE.  If you are going
   to want online help you will also need to copy DELPHI.HDX to 
   DELPHIPM.HDX and DELPHI.HLP to DELPHIPM.HLP.
   
2) Make DelphiPM read TPP.TPL:
   - Load DELPHIPM.EXE into a hex file editor (such an NU's DISKEDIT.EXE)
   - Goto offset 0392AD.  You should see "[0A]DELPHI.DSL"
   - Change the bytes starting at 0392AD to 
       07 54 50 50 2E 54 50 4C 00 00 00   ([07]TPP.TPL[00][00][00])
   - Goto offset 063C6F.  You should see:
       89 26 24 2C 26 C7 05 04 00 8A 46 0C
     This instruction (26C7050400) is where Delphi loads the flag
     determining which mode to compile in.  (0004=Windows,0002=DPMI)
     (don't ask how I found it... it was one of those 3 hour midnight
     debugging projects)
   - Change offset 063C76 to 02.
   - Save your work.
   
3) Go into Windows and try it out (Don't forget to execute DELPHIPM and
   not DELPHI).  It is really nice to be able to use features such as
   syntax highlighting, object browser, search for error address, project
   manager, etc. while working on DOS programs.  You can compile your 
   programs with Ctrl-F9.  If you uncheck "Integrated Debugging" in the
   Environment options you can pressing F9 to run the program.
   
** In theory I suppose you could have the IDE compile DOS real mode
programs by changing DELPHI.DSL to TURBO.TPL and the 04 byte to 01 but
I have not tried this.


---------------------------------------------------------------------------
VII. Debugging Delphi DOS Programs ----------------------------------------
---------------------------------------------------------------------------

Sorry, no integrated debugging (yet?).  But you can use Turbo 
Debugger 4.6 (TD.EXE) (maybe even 4.0 or 4.5, I don't know.  I do know
that 3.x give an "invalid linker" error) to debug your DELPHI DOS DPMI 
programs!  Make sure you have TDXDEBUG.DLL (It should be in you \BP\BIN) 
because it was not included on my C++ CD-ROM.
   
Add TD.EXE to your DELPHIPM's Tools menu by using "Options->Tools".  Set
the parameters to be: $EXENAME $PARAMS $TDW $SAVEALL.  This will cause
Delphi to recompile the project with external debug info before executing
TD.EXE.


---------------------------------------------------------------------------
VIII. Notes ---------------------------------------------------------------
---------------------------------------------------------------------------

1) Data Types, some of which are undocumented
   Name     Size
   -------- ----
   ShortInt  8
  *SmallInt  16  (stays 16 in 32bit-compiler)
   Integer   16  (32 in 32bit-compiler)
   LongInt   32
   Byte      8
   Word      16
  *Cardinal  16  (32 in 32bit-compiler)
   Boolean   8
  *Bool      16  (? in 32bit-compiler)
  *ByteBool  8
  *WordBool  16
  *LongBool  32
 
   If we could only get Borland to give us LongWord(32), HugeInt(64),
   and HugeWord(64)!
   
2) How to convert a .DFM to text (does what CONVERT.EXE does):
    var
      xxx : TStream;
      yyy : TStream;
    begin
      xxx := nil;
      yyy := nil;
      try
        try
          xxx := TFileStream.Create( ParamStr(1), fmOpenRead );
          yyy := TFileStream.Create( 'OUTPUT.TXT', fmCreate );
        except
          WriteLn( 'Failed creating OUTPUT.TXT' );
          exit;
        end;
        ObjectResourceToText( xxx, yyy );
      finally
        xxx.Free;
        yyy.Free;
      end;
    end;


---------------------------------------------------------------------------
IX. Disclaimer ------------------------------------------------------------
---------------------------------------------------------------------------

Use at your own risk. I don't make any claims about the EXEs created as 
this is not official from Borland.  (I hate to scare anyone off though,
I have not experience any problems.)
  
Kevin R. Smith
73632.712@compuserve.com
