home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / SYSTEM / DEVLOD.ZIP / DEVLOD.FIG < prev    next >
Encoding:
Text File  |  1990-12-06  |  26.5 KB  |  839 lines

  1. Figure 1: Basic Structure of DEVLOD
  2.  
  3.  
  4. startup code (C0.ASM)
  5. main (DEVLOD.C)
  6.     Move_Loader
  7.         movup (MOVUP.ASM)
  8.     Load_Drvr
  9.         INT 21h Function 4B03h (Load Overlay)
  10.     Get_List
  11.         INT 21h Function 52h (Get List of Lists)
  12.         based on DOS version number:
  13.             get number of block devices
  14.             get value of LASTDRIVE
  15.             get Current Directory Structure (CDS) base
  16.             get pointer to NUL device
  17.     Init_Drvr
  18.         call DD init routine
  19.             build command packet
  20.             call Strategy
  21.             call Interrupt
  22.     Get_Out
  23.         if block device:
  24.             Put_Blk_Dev
  25.                 for each unit:
  26.                     Next_Drive
  27.                         get next available drive letter
  28.                     INT 21h Function 32h (Get DPB)
  29.                     INT 21h Function 53h (Translate BPB -> DPB)
  30.                     poke CDS
  31.                     link into DPB chain
  32.         Fix_DOS_Chain
  33.             link into dev chain
  34.         release environment space
  35.         INT 21h Function 31h (TSR)
  36.  
  37.  
  38. -----------------------------------------------------------------------------
  39.  
  40. Figure 2: Loading Device Drivers
  41.  
  42.  
  43. C:\UNDOC\KYLE>devlod \dos\smartdrv.sys 256 /a
  44. Microsoft SMARTDrive Disk Cache version 3.03
  45.     Cache size: 256K in Expanded Memory
  46.     Room for 30 tracks of 17 sectors each
  47.     Minimum cache size will be 0K
  48.  
  49. C:\UNDOC\KYLE>devlod \dos\ramdrive.sys
  50. Microsoft RAMDrive version 3.04 virtual disk D:
  51.     Disk size: 64k
  52.     Sector size: 512 bytes
  53.     Allocation unit: 1 sectors
  54.     Directory entries: 64
  55.  
  56. C:\UNDOC\KYLE>devlod \dos\vdisk.sys
  57. VDISK Version 3.2 virtual disk E:
  58.    Buffer size adjusted
  59.    Sector size adjusted
  60.    Directory entries adjusted
  61.    Buffer size:         64 KB
  62.    Sector size:        128
  63.    Directory entries:   64
  64.  
  65. C:\UNDOC\KYLE>devlod \dos\ansi.sys
  66.  
  67. C:\UNDOC\KYLE>mem
  68. Seg     Owner   Size           Env
  69. 09F3    0008    00F4 (  3904)        config [15 2F 4B 67 ]
  70. 0AE8    0AE9    00D3 (  3376)  0BC1  c:\dos33\command.com   [22 23 24 2E ]
  71. 0BBC    0000    0003 (    48)        free 
  72. 0BC0    0AE9    0019 (   400)            
  73. 0BDA    0AE9    0004 (    64)            
  74. 0BDF    3074    000D (   208)            
  75. 0BED    0000    0000 (     0)        free 
  76. 0BEE    0BEF    0367 ( 13936)  0BE0   \msc\bin\smartdrv.sys 256 /a [13 19 ]
  77. 0F56    0F57    1059 ( 66960)  0BE0   \msc\bin\ramdrive.sys  [F1 FA ]
  78. 1FB0    1FB1    104C ( 66752)  0BE0   \dos33\vdisk.sys  
  79. 2FFD    2FFE    0075 (  1872)  0BE0   \dos33\ansi.sys  [1B 29 ]
  80. 3073    3074    1218 ( 74112)  0BE0  C:\UNDOC\KYLE\MEM.EXE   [00 ]
  81. 428C    0000    7573 (481072)        free [30 F8 ]
  82.  
  83. C:\UNDOC\KYLE>dev
  84. NUL     
  85. CON     
  86. Block: 1 unit(s)
  87. Block: 1 unit(s)
  88. SMARTAAR
  89. QEMM386$
  90. EMMXXXX0
  91. CON     
  92. AUX     
  93. PRN     
  94. CLOCK$  
  95. Block: 3 unit(s)
  96. COM1    
  97. LPT1    
  98. LPT2    
  99. LPT3    
  100. COM2    
  101. COM3    
  102. COM4
  103.  
  104. -----------------------------------------------------------------------------
  105.  
  106. Sidebar
  107.  
  108. Some DOS and BIOS Data Structures
  109.  
  110.  
  111. BPB     BIOS uses the BPB (Bios Parameter Block) to learn the format of a
  112.         block device. Normally, the BPB is part of a physical disk's boot
  113.         record, and contains information such as the number of bytes
  114.         in a sector, the number of root directory entries, the number of
  115.         sectors taken by the File Allocation Table (FAT), etc.
  116.  
  117. CDS     The CDS (Current Directory Structure) is an undocumented array of
  118.         structures, sometimes also called the Drive Info Table, which
  119.         maintains the current state of each drive in the system. The array is
  120.         n elements long, where n equals LASTDRIVE.
  121.  
  122. DPB     For every block device (disk drive) in the system, there is a DPB
  123.         (Drive Parameter Block). These 32-byte blocks contain the information
  124.         that DOS uses to convert cluster numbers into Logical Sector Numbers,
  125.         and also associate the device driver for that device with its
  126.         assigned drive letter.
  127.  
  128. LoL     Probably the most commonly used undocumented DOS data structure,
  129.         the List of Lists is the DOS internal variable table, which includes,
  130.         amongst other things, the LASTDRIVE value, the head of the device
  131.         driver chain, and the CDS (Current Directory Structure). A pointer to
  132.         the LoL is returned in ES:BX by undocumented DOS Int 21h Function
  133.         52h.
  134.  
  135. ----------------------------------------------------------------------------
  136.  
  137. Listing 1
  138.  
  139.  
  140. /********************************************************************
  141.  *     DEVLOD.C - Jim Kyle - 08/20/90                               *
  142.  *            Copyright 1990 by Jim Kyle - All Rights Reserved      *
  143.  *     (minor revisions by Andrew Schulman - 9/12/90                *
  144.  *     Dynamic loader for device drivers                            *
  145.  *            Requires Turbo C; see DEVLOD.MAK also for ASM helpers.*
  146.  ********************************************************************/
  147.      
  148. #include <stdio.h>
  149. #include <stdlib.h>
  150. #include <dos.h>
  151.  
  152. typedef unsigned char BYTE;
  153.  
  154. #define GETFLAGS __emit__(0x9F)     /* if any error, quit right now */
  155. #define FIXDS    __emit__(0x16,0x1F)/* PUSH SS, POP DS              */
  156. #define PUSH_BP  __emit__(0x55)
  157. #define POP_BP   __emit__(0x5D)
  158.  
  159. unsigned _stklen = 0x200;
  160. unsigned _heaplen = 0;
  161.  
  162. char FileName[65];  /* filename global buffer                       */
  163. char * dvrarg;      /* points to char after name in cmdline buffer  */
  164. unsigned movsize;   /* number of bytes to be moved up for driver    */
  165. void (far * driver)();  /* used as pointer to call driver code      */
  166. void far * drvptr;  /* holds pointer to device driver               */
  167. void far * nuldrvr; /* additional driver pointers                   */
  168. void far * nxtdrvr;
  169. BYTE far * nblkdrs; /* points to block device count in List of Lists*/
  170. unsigned lastdrive; /* value of LASTDRIVE in List of Lists          */
  171. BYTE far * CDSbase; /* base of Current Dir Structure                */
  172. int CDSsize;        /* size of CDS element                          */
  173. unsigned nulseg;    /* hold parts of ListOfLists pointer            */
  174. unsigned nulofs;
  175. unsigned LoLofs;
  176.  
  177. #pragma pack(1)
  178.  
  179. struct packet{      /* device driver's command packet               */
  180.     BYTE hdrlen;
  181.     BYTE unit;
  182.     BYTE command;       /* 0 to initialize      */
  183.     unsigned status;    /* 0x8000 is error      */
  184.     BYTE reserv[8];
  185.     BYTE nunits;
  186.     unsigned brkofs;    /* break adr on return  */
  187.     unsigned brkseg;    /* break seg on return  */
  188.     unsigned inpofs;    /* SI on input          */
  189.     unsigned inpseg;    /* _psp on input        */
  190.     BYTE NextDrv;       /* next available drive */
  191.   } CmdPkt;
  192.   
  193. typedef struct {    /* Current Directory Structure (CDS)            */
  194.     BYTE path[0x43];
  195.     unsigned flags;
  196.     void far *dpb;
  197.     unsigned start_cluster;
  198.     unsigned long ffff;
  199.     unsigned slash_offset;  /* offset of '\' in current path field  */
  200.     // next for DOS4+ only
  201.     BYTE unknown;
  202.     void far *ifs;
  203.     unsigned unknown2;
  204.     } CDS;
  205.  
  206. extern unsigned _psp;       /* established by startup code in c0    */
  207. extern unsigned _heaptop;   /* established by startup code in c0    */
  208. extern BYTE _osmajor;       /* established by startup code      */
  209. extern BYTE _osminor;       /* established by startup code      */
  210.  
  211. void _exit( int );          /* established by startup code in c0    */
  212. void abort( void );         /* established by startup code in c0    */
  213.  
  214. void movup( char far *, char far *, int ); /* in MOVUP.ASM file     */
  215. void copyptr( void far *src, void far *dst ); /* in MOVUP.ASM file  */
  216.  
  217. void exit(int c)            /* called by startup code's sequence    */
  218. { _exit(c);}
  219.  
  220. int Get_Driver_Name ( void )
  221. { char *nameptr;
  222.   int i, j, cmdlinesz;
  223.  
  224.   nameptr = (char *)0x80;   /* check command line for driver name   */
  225.   cmdlinesz = (unsigned)*nameptr++;
  226.   if (cmdlinesz < 1)        /* if nothing there, return FALSE       */
  227.     return 0;
  228.   for (i=0; i<cmdlinesz && nameptr[i]<'!'; i++) /* skip blanks      */
  229.     ;
  230.   dvrarg = (char *)&nameptr[i]; /* save to put in SI                */
  231.   for ( j=0; i<cmdlinesz && nameptr[i]>' '; i++)    /* copy name    */
  232.     FileName[j++] = nameptr[i];
  233.   FileName[j] = '\0';
  234.  
  235.   return 1;                 /* and return TRUE to keep going        */
  236. }
  237.  
  238. void Put_Msg ( char *msg )
  239. #ifdef INT29
  240.     /* gratuitous use of undocumented DOS */
  241.     while (*msg)
  242.     { _AL = *msg++;             /* MOV AL,*msg  */
  243.       geninterrupt(0x29);       /* INT 29h */
  244.     }
  245. #else
  246.     _AH = 2;    /* doesn't need to be inside loop */
  247.     while (*msg)
  248.     { _DL = *msg++;            
  249.       geninterrupt(0x21);
  250.     }
  251. #endif
  252. }
  253.  
  254. void Err_Halt ( char *msg )     /* print message and abort          */
  255. { Put_Msg ( msg );
  256.   Put_Msg ( "\r\n" );           /* send CR,LF   */
  257.   abort();
  258. }
  259.  
  260. void Move_Loader ( void )       /* vacate lower part of RAM         */
  261. {
  262.     unsigned movsize, destseg;
  263.     movsize = _heaptop - _psp; /* size of loader in paragraphs      */
  264.     destseg = *(unsigned far *)MK_FP( _psp, 2 ); /* end of memory   */
  265.     movup ( MK_FP( _psp, 0 ), MK_FP( destseg - movsize, 0 ),
  266.             movsize << 4);      /* move and fix segregs             */
  267. }
  268.  
  269. void Load_Drvr ( void )         /* load driver file into RAM    */
  270. { unsigned handle;
  271.   struct {
  272.     unsigned LoadSeg;
  273.     unsigned RelocSeg;
  274.   } ExecBlock;
  275.  
  276.   ExecBlock.LoadSeg = _psp + 0x10;
  277.   ExecBlock.RelocSeg = _psp + 0x10;
  278.   _DX = (unsigned)&FileName[0];
  279.   _BX = (unsigned)&ExecBlock;
  280.   _ES = _SS;                    /* es:bx point to ExecBlock     */
  281.   _AX = 0x4B03;                 /* load overlay                 */
  282.   geninterrupt ( 0x21 );        /* DS is okay on this call      */
  283.   GETFLAGS;
  284.   if ( _AH & 1 )
  285.     Err_Halt ( "Unable to load driver file." );
  286. }
  287.  
  288. void Get_List ( void )          /* set up pointers via List     */
  289. { _AH = 0x52;                   /* find DOS List of Lists       */
  290.   geninterrupt ( 0x21 );
  291.   nulseg = _ES;                 /* DOS data segment             */
  292.   LoLofs = _BX;                 /* current drive table offset   */
  293.  
  294.   switch( _osmajor )            /* NUL adr varies with version  */
  295.     {
  296.     case  0:
  297.       Err_Halt ( "Drivers not used in DOS V1." );
  298.     case  2:
  299.       nblkdrs = NULL;
  300.       nulofs = LoLofs + 0x17;
  301.       break;
  302.     case  3:
  303.       if (_osminor == 0)
  304.       {
  305.           nblkdrs = (BYTE far *) MK_FP(nulseg, LoLofs + 0x10);
  306.           lastdrive = *((BYTE far *) MK_FP(nulseg, LoLofs + 0x1b));
  307.           nulofs = LoLofs + 0x28;
  308.       }
  309.       else
  310.       {
  311.           nblkdrs = (BYTE far *) MK_FP(nulseg, LoLofs + 0x20);
  312.           lastdrive = *((BYTE far *) MK_FP(nulseg, LoLofs + 0x21));
  313.           nulofs = LoLofs + 0x22;
  314.       }
  315.       CDSbase = *(BYTE far * far *)MK_FP(nulseg, LoLofs + 0x16);
  316.       CDSsize = 81;
  317.       break;
  318.     case  4:
  319.     case  5:
  320.       nblkdrs = (BYTE far *) MK_FP(nulseg, LoLofs + 0x20);
  321.       lastdrive = *((BYTE far *) MK_FP(nulseg, LoLofs + 0x21));
  322.       nulofs  = LoLofs + 0x22;
  323.       CDSbase = *(BYTE far * far *) MK_FP(nulseg, LoLofs + 0x16);
  324.       CDSsize = 88;
  325.       break;
  326.     case 10:
  327.     case 20:
  328.       Err_Halt ( "OS2 DOS Box not supported." );
  329.     default:
  330.       Err_Halt ( "Unknown version of DOS!");
  331.     }
  332. }
  333.  
  334. void Fix_DOS_Chain ( void )     /* patches driver into DOS chn  */
  335. { unsigned i;
  336.  
  337.   nuldrvr = MK_FP( nulseg, nulofs+0x0A ); /* verify the drvr    */
  338.   drvptr = "NUL     ";
  339.   for ( i=0; i<8; ++i )
  340.     if ( *((BYTE far *)nuldrvr+i) != *((BYTE far *)drvptr+i) )
  341.       Err_Halt ( "Failed to find NUL driver." );
  342.  
  343.   nuldrvr = MK_FP( nulseg, nulofs );    /* point to NUL driver  */
  344.   drvptr  = MK_FP( _psp+0x10, 0 );      /* new driver's address */
  345.  
  346.   copyptr ( nuldrvr, &nxtdrvr );        /* hold old head now    */
  347.   copyptr ( &drvptr, nuldrvr );         /* put new after NUL    */
  348.   copyptr ( &nxtdrvr, drvptr );         /* and old after new    */
  349. }
  350.  
  351. // returns number of next free drive, -1 if none available
  352. int Next_Drive ( void )
  353. {
  354. #ifdef USE_BLKDEV
  355.   /* This incorrectly bashes SUBSTed and network drives */
  356.   return (nblkdrs && (*nblkdrs < lastdrive)) ? *nblkdrs : -1;
  357. #else
  358.   /* The following approach takes account of SUBSTed and
  359.      network-redirector drives */
  360.   CDS far *cds;
  361.   int i;
  362.   /* find first unused entry in CDS structure */
  363.   for (i=0, cds=CDSbase; i<lastdrive; i++, ((BYTE far *)cds)+=CDSsize)
  364.     if (! cds->flags)                /* found a free drive  */
  365.         break;
  366.   return (i == lastdrive) ? -1 : i; 
  367. #endif  
  368. }
  369.  
  370. int Init_Drvr ( void )
  371. { unsigned tmp;
  372. #define INIT 0
  373.   CmdPkt.command = INIT;        /* build command packet         */
  374.   CmdPkt.hdrlen = sizeof (struct packet);
  375.   CmdPkt.unit = 0;
  376.   CmdPkt.inpofs  = (unsigned)dvrarg;    /* points into cmd line */
  377.   CmdPkt.inpseg  = _psp;
  378.   /* can't really check for next drive here, because don't yet know
  379.      if this is a block driver or not */
  380.   CmdPkt.NextDrv = Next_Drive();
  381.   drvptr  = MK_FP( _psp+0x10, 0 );      /* new driver's address */
  382.  
  383.   tmp = *((unsigned far *)drvptr+3);    /* STRATEGY pointer     */
  384.   driver = MK_FP( FP_SEG( drvptr ), tmp );
  385.   _ES = FP_SEG( (void far *)&CmdPkt );
  386.   _BX = FP_OFF( (void far *)&CmdPkt );
  387.   (*driver)();                  /* set up the packet address    */
  388.  
  389.   tmp = *((unsigned far *)drvptr+4);    /* COMMAND pointer      */
  390.   driver = MK_FP( FP_SEG( drvptr ), tmp );
  391.   (*driver)();                  /* do the initialization        */
  392.   
  393.   /* check status code in command packet                        */
  394.   return (! ( CmdPkt.status & 0x8000 ));
  395. }
  396.  
  397. int  Put_Blk_Dev ( void )   /* TRUE if Block Device failed      */
  398. { int newdrv;
  399.   int retval = 1;           /* pre-set for failure              */
  400.   int unit = 0;
  401.   BYTE far *DPBlink;
  402.   CDS far *cds;
  403.   int i;
  404.  
  405.   if ((Next_Drive() == -1) || CmdPkt.nunits == 0)
  406.     return retval;          /* cannot install block driver      */
  407.   if (CmdPkt.brkofs != 0)   /* align to next paragraph          */
  408.   {
  409.     CmdPkt.brkseg += (CmdPkt.brkofs >> 4) + 1;
  410.     CmdPkt.brkofs = 0;
  411.   }
  412.   while( CmdPkt.nunits-- )
  413.   {
  414.     if ((newdrv = Next_Drive()) == -1)
  415.         return 1;
  416.     (*nblkdrs)++;
  417.     _AH = 0x32;             /* get last DPB and set poiner      */
  418.     _DL = newdrv;
  419.     geninterrupt ( 0x21 );
  420.     _AX = _DS;              /* save segment to make the pointer */
  421.     FIXDS;
  422.     DPBlink = MK_FP(_AX, _BX);
  423.     (unsigned) DPBlink += (_osmajor < 4 ? 24 : 25 );
  424.     _SI = *(unsigned far *)MK_FP(CmdPkt.inpseg, CmdPkt.inpofs);
  425.     _ES = CmdPkt.brkseg;
  426.     _DS = CmdPkt.inpseg;
  427.     _AH = 0x53;
  428.     PUSH_BP;
  429.     _BP = 0;
  430.     geninterrupt ( 0x21 );    /* build the DPB for this unit    */
  431.     POP_BP;
  432.     FIXDS;
  433.     *(void far * far *)DPBlink = MK_FP( CmdPkt.brkseg, 0 );
  434.  
  435.     /* set up the Current Directory Structure for this drive */
  436.     cds = (CDS far *) (CDSbase + (newdrv * CDSsize));
  437.     cds->flags = 1 << 14;       /* PHYSICAL DRIVE */
  438.     cds->dpb = MK_FP(CmdPkt.brkseg, 0);
  439.     cds->start_cluster = 0xFFFF;
  440.     cds->ffff = -1L;
  441.     cds->slash_offset = 2;
  442.     if (_osmajor > 3)
  443.       { cds->unknown = 0;
  444.         cds->ifs = (void far *) 0;
  445.         cds->unknown2 = 0;
  446.       }
  447.       
  448.     /* set up pointers for DPB, driver  */
  449.     DPBlink = MK_FP( CmdPkt.brkseg, 0);
  450.     *DPBlink = newdrv;
  451.     *(DPBlink+1) = unit++;
  452.     if (_osmajor > 3)
  453.       DPBlink++;          /* add one if DOS 4                 */
  454.     *(long far *)(DPBlink+0x12) = (long)MK_FP( _psp+0x10, 0 );
  455.     *(long far *)(DPBlink+0x18) = 0xFFFFFFFF;
  456.     CmdPkt.brkseg += 2;       /* Leave two paragraphs for DPB   */
  457.     CmdPkt.inpofs += 2;       /* Point to next BPB pointer      */
  458.   }     /* end of nunits loop   */
  459.   return 0;                 /* all went okay                    */
  460. }
  461.  
  462. void Get_Out ( void )
  463. { unsigned temp;
  464.  
  465.   temp = *((unsigned far *)drvptr+2);   /* attribute word       */
  466.   if ((temp & 0x8000) == 0 )    /* if block device, set up tbls */
  467.     if (Put_Blk_Dev() )
  468.       Err_Halt( "Could not install block device" );
  469.  
  470.   Fix_DOS_Chain ();             /* else patch it into DOS       */
  471.  
  472.   _ES = *((unsigned *)MK_FP( _psp, 0x002C ));
  473.   _AH = 0x49;                   /* release environment space    */
  474.   geninterrupt ( 0x21 );
  475.  
  476.   /* then set up regs for KEEP function, and go resident        */
  477.   temp = (CmdPkt.brkofs + 15);  /* normalize the offset         */
  478.   temp >>= 4;
  479.   temp += CmdPkt.brkseg;        /* add the segment address      */
  480.   temp -= _psp;                 /* convert to paragraph count   */
  481.   _AX = 0x3100;                 /* KEEP function of DOS         */
  482.   _DX = (unsigned)temp;         /* paragraphs to retain         */
  483.   geninterrupt ( 0x21 );        /* won't come back from here!   */
  484. }
  485.  
  486. void main ( void )
  487. { if (!Get_Driver_Name() )
  488.     Err_Halt ( "Device driver name required.");
  489.   Move_Loader ();               /* move code high and jump      */
  490.   Load_Drvr ();                 /* bring driver into freed RAM  */
  491.   Get_List();                   /* get DOS internal variables   */
  492.   if (Init_Drvr ())             /* let driver do its thing      */
  493.       Get_Out();                /* check init status, go TSR    */
  494.   else
  495.       Err_Halt ( "Driver initialization failed." );
  496. }
  497.  
  498. ------------------------------------------------------------------------------
  499.  
  500. Listing 2
  501.  
  502.  
  503.         NAME    movup
  504. ;[]------------------------------------------------------------[]
  505. ;|      MOVUP.ASM -- helper code for DEVLOD.C                   |
  506. ;|      Copyright 1990 by Jim Kyle - All Rights Reserved        |
  507. ;[]------------------------------------------------------------[]
  508.  
  509. _TEXT   SEGMENT BYTE PUBLIC 'CODE'
  510. _TEXT   ENDS
  511.  
  512. _DATA   SEGMENT WORD PUBLIC 'DATA'
  513. _DATA   ENDS
  514.  
  515. _BSS    SEGMENT WORD PUBLIC 'BSS'
  516. _BSS    ENDS
  517.  
  518. DGROUP  GROUP   _TEXT, _DATA, _BSS
  519.  
  520. ASSUME  CS:_TEXT, DS:DGROUP
  521.  
  522. _TEXT   SEGMENT BYTE PUBLIC 'CODE'
  523.  
  524. ;-----------------------------------------------------------------
  525. ;       movup( src, dst, nbytes )
  526. ;       src and dst are far pointers. area overlap is NOT okay
  527. ;-----------------------------------------------------------------
  528.         PUBLIC  _movup
  529.  
  530. _movup  PROC      NEAR
  531.         push    bp
  532.         mov     bp, sp
  533.         push    si
  534.         push    di
  535.         lds     si,[bp+4]               ; source
  536.         les     di,[bp+8]               ; destination
  537.         mov     bx,es                   ; save dest segment
  538.         mov     cx,[bp+12]              ; byte count
  539.         cld
  540.         rep     movsb                   ; move everything to high ram
  541.         mov     ss,bx                   ; fix stack segment ASAP
  542.         mov     ds,bx                   ; adjust DS too
  543.         pop     di
  544.         pop     si
  545.         mov     sp, bp
  546.         pop     bp
  547.         pop     dx                      ; Get return address
  548.         push    bx                      ; Put segment up first
  549.         push    dx                      ; Now a far address on stack
  550.         retf
  551. _movup  ENDP
  552.  
  553. ;-------------------------------------------------------------------
  554. ;       copyptr( src, dst )
  555. ;       src and dst are far pointers.
  556. ;       moves exactly 4 bytes from src to dst.
  557. ;-------------------------------------------------------------------
  558.         PUBLIC  _copyptr
  559.  
  560. _copyptr        PROC      NEAR
  561.         push    bp
  562.         mov     bp, sp
  563.         push    si
  564.         push    di
  565.         push    ds
  566.         lds     si,[bp+4]               ; source
  567.         les     di,[bp+8]               ; destination
  568.         cld
  569.         movsw
  570.         movsw
  571.         pop     ds
  572.         pop     di
  573.         pop     si
  574.         mov     sp, bp
  575.         pop     bp
  576.         ret
  577. _copyptr        ENDP
  578.  
  579. _TEXT   ENDS
  580.  
  581.         end
  582.  
  583.  
  584.  
  585. ------------------------------------------------------------------------------
  586.  
  587. Listing 3
  588.  
  589.  
  590.         NAME c0
  591. ;[]------------------------------------------------------------[] 
  592. ;|  C0.ASM -- Start Up Code                                     | 
  593. ;| based on Turbo-C startup code, extensively modified          |
  594. ;[]------------------------------------------------------------[]
  595.  
  596. _TEXT   SEGMENT BYTE PUBLIC 'CODE'
  597. _TEXT   ENDS
  598.  
  599. _DATA   SEGMENT WORD PUBLIC 'DATA'
  600. _DATA   ENDS
  601.  
  602. _BSS    SEGMENT WORD PUBLIC 'BSS'
  603. _BSS    ENDS
  604.  
  605. DGROUP  GROUP   _TEXT, _DATA, _BSS
  606.  
  607. ;       External References
  608.  
  609. EXTRN   _main : NEAR
  610. EXTRN   _exit : NEAR
  611.  
  612. EXTRN   __stklen : WORD
  613. EXTRN   __heaplen : WORD
  614.  
  615. PSPHigh         equ     00002h
  616. PSPEnv          equ     0002ch
  617.  
  618. MINSTACK        equ     128     ; minimal stack size in words
  619.  
  620. ;       At the start, DS, ES, and SS are all equal to CS
  621.  
  622. ;/*-----------------------------------------------------*/
  623. ;/*     Start Up Code                                   */
  624. ;/*-----------------------------------------------------*/
  625.  
  626. _TEXT   SEGMENT BYTE PUBLIC 'CODE'
  627.  
  628. ASSUME  CS:_TEXT, DS:DGROUP
  629.  
  630.         ORG     100h
  631.  
  632. STARTX  PROC    NEAR
  633.  
  634.         mov     dx, cs          ; DX = GROUP Segment address
  635.         mov     DGROUP@, dx
  636.         mov     ah, 30h         ; get DOS version
  637.         int     21h
  638.         mov     bp, ds:[PSPHigh]; BP = Highest Memory Segment Addr
  639.         mov     word ptr __heaptop, bp
  640.         mov     bx, ds:[PSPEnv] ; BX = Environment Segment address
  641.         mov     __version, ax   ; Keep major and minor version number
  642.         mov     __psp, es       ; Keep Program Segment Prefix address
  643.  
  644. ;       Determine the amount of memory that we need to keep
  645.  
  646.         mov     dx, ds          ; DX = GROUP Segment address
  647.         sub     bp, dx          ; BP = remaining size in paragraphs
  648.         mov     di, __stklen    ; DI = Requested stack size
  649. ;
  650. ; Make sure that the requested stack size is at least MINSTACK words.
  651. ;
  652.         cmp     di, 2*MINSTACK  ; requested stack big enough ?
  653.         jae     AskedStackOK    ; yes, use it
  654.         mov     di, 2*MINSTACK  ; no,  use minimal value
  655.         mov     __stklen, di    ; override requested stack size
  656. AskedStackOK:
  657.         add     di, offset DGROUP: edata
  658.         jb      InitFailed      ; DATA segment can NOT be > 64 Kbytes
  659.         add     di, __heaplen
  660.         jb      InitFailed      ; DATA segment can NOT be > 64 Kbytes
  661.         mov     cl, 4
  662.         shr     di, cl          ; $$$ Do not destroy CL $$$
  663.         inc     di              ; DI = DS size in paragraphs
  664.         cmp     bp, di
  665.         jnb     TooMuchRAM      ; Enough to run the program
  666.  
  667. ;       All initialization errors arrive here
  668.  
  669. InitFailed:
  670.         jmp     near ptr _abort
  671.  
  672. ;       Set heap base and pointer
  673.  
  674. TooMuchRAM:
  675.         mov     bx, di          ; BX = total paragraphs in DGROUP
  676.         shl     di, cl          ; $$$ CX is still equal to 4 $$$
  677.         add     bx, dx          ; BX = seg adr past DGROUP
  678.         mov     __heapbase, bx
  679.         mov     __brklvl, bx
  680. ;
  681. ;       Set the program stack down into RAM that will be kept.
  682. ;
  683.         cli
  684.         mov     ss, dx          ; DGROUP
  685.         mov     sp, di          ; top of (reduced) program area
  686.         sti
  687.  
  688.         mov     bx,__heaplen    ; set up heap top pointer
  689.         add     bx,15
  690.         shr     bx,cl           ; length in paragraphs
  691.         add     bx,__heapbase
  692.         mov     __heaptop, bx
  693. ;
  694. ;       Clear uninitialized data area to zeroes
  695. ;
  696.         xor     ax, ax
  697.         mov     es, cs:DGROUP@
  698.         mov     di, offset DGROUP: bdata
  699.         mov     cx, offset DGROUP: edata
  700.         sub     cx, di
  701.         rep     stosb
  702. ;
  703. ;       exit(main());
  704. ;
  705.         call    _main           ; the real C program
  706.         push    ax
  707.         call    _exit           ; part of the C program too
  708.  
  709. ;----------------------------------------------------------------
  710. ;       _exit()
  711. ;       Restore interrupt vector taken during startup.
  712. ;       Exit to DOS.
  713. ;----------------------------------------------------------------
  714.  
  715.         PUBLIC  __exit
  716. __exit  PROC      NEAR
  717.         push    ss
  718.         pop     ds
  719.  
  720. ;       Exit to DOS
  721.  
  722. ExitToDOS:
  723.         mov     bp,sp
  724.         mov     ah,4Ch
  725.         mov     al,[bp+2]
  726.         int     21h                     ; Exit to DOS
  727.  
  728. __exit  ENDP
  729.  
  730. STARTX  ENDP
  731.  
  732. ;[]------------------------------------------------------------[]
  733. ;|      Miscellaneous functions                                 |
  734. ;[]------------------------------------------------------------[]
  735.  
  736. ErrorDisplay    PROC    NEAR
  737.                 mov     ah, 040h
  738.                 mov     bx, 2           ; stderr device
  739.                 int     021h
  740.                 ret
  741. ErrorDisplay    ENDP
  742.  
  743.         PUBLIC  _abort
  744. _abort  PROC      NEAR
  745.                 mov     cx, lgth_abortMSG
  746.                 mov     dx, offset DGROUP: abortMSG
  747. MsgExit3        label   near
  748.                 push    ss
  749.                 pop     ds
  750.                 call    ErrorDisplay
  751. CallExit3       label   near
  752.                 mov     ax, 3
  753.                 push    ax
  754.                 call    __exit          ; _exit(3);
  755. _abort   ENDP
  756.  
  757. ;       The DGROUP@ variable is used to reload DS with DGROUP
  758.  
  759.         PUBLIC  DGROUP@
  760. DGROUP@     dw    ?
  761.  
  762. _TEXT   ENDS
  763.  
  764. ;[]------------------------------------------------------------[]
  765. ;|      Start Up Data Area                                      |
  766. ;[]------------------------------------------------------------[]
  767.  
  768. _DATA   SEGMENT WORD PUBLIC 'DATA'
  769.  
  770. abortMSG        db      'Quitting program...', 13, 10
  771. lgth_abortMSG   equ     $ - abortMSG
  772.  
  773. ;
  774. ;                       Miscellaneous variables
  775. ;
  776.         PUBLIC  __psp
  777.         PUBLIC  __version
  778.         PUBLIC  __osmajor
  779.         PUBLIC  __osminor
  780.  
  781. __psp           dw      0
  782. __version       label   word
  783. __osmajor       db      0
  784. __osminor       db      0
  785.  
  786. ;       Memory management variables
  787.  
  788.         PUBLIC  ___heapbase
  789.         PUBLIC  ___brklvl
  790.         PUBLIC  ___heaptop
  791.         PUBLIC  __heapbase
  792.         PUBLIC  __brklvl
  793.         PUBLIC  __heaptop
  794.  
  795. ___heapbase     dw      DGROUP:edata
  796. ___brklvl       dw      DGROUP:edata
  797. ___heaptop      dw      DGROUP:edata
  798. __heapbase      dw      0
  799. __brklvl        dw      0
  800. __heaptop       dw      0
  801.  
  802. _DATA   ENDS
  803.  
  804. _BSS    SEGMENT WORD PUBLIC 'BSS'
  805.  
  806. bdata   label   byte
  807. edata   label   byte            ; mark top of used area
  808.  
  809. _BSS    ENDS
  810.  
  811.         END     STARTX
  812.  
  813.  
  814. ------------------------------------------------------------------------------
  815.  
  816. Listing 4
  817.  
  818.  
  819. # makefile for DEVLOD.COM - last revised 05/23/90 - jk
  820. # can substitute other assemblers for TASM
  821.  
  822. c0.obj  :       c0.asm
  823.         tasm c0 /t/mx/la;
  824.  
  825. movup.obj:      movup.asm
  826.         tasm movup /t/mx/la;
  827.  
  828. devlod.obj:     devlod.c
  829.         tcc -c -ms devlod
  830.  
  831. devlod.com:     devlod.obj c0.obj movup.obj
  832.         tlink c0 movup devlod /c/m,devlod
  833.         if exist devlod.com del devlod.com
  834.         exe2bin devlod.exe devlod.com
  835.         del devlod.exe
  836.  
  837.  
  838.