home *** CD-ROM | disk | FTP | other *** search
/ Computer Select (Limited Edition) / Computer Select.iso / dobbs / v16n11 / devlod.asc next >
Encoding:
Text File  |  1991-11-15  |  21.8 KB  |  696 lines

  1. _LOADING DEVICE DRIVERS FROM THE DOS COMMAND LINE_
  2. by Jim Kyle
  3.  
  4. [LISTING ONE]
  5.  
  6. /********************************************************************
  7.  *     DEVLOD.C - Copyright 1990 by Jim Kyle - All Rights Reserved  *
  8.  *     (minor revisions by Andrew Schulman                             *
  9.  *     Dynamic loader for device drivers                            *
  10.  *            Requires Turbo C; see DEVLOD.MAK also for ASM helpers.*
  11.  ********************************************************************/
  12.      
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <dos.h>
  16.  
  17. typedef unsigned char BYTE;
  18.  
  19. #define GETFLAGS __emit__(0x9F)     /* if any error, quit right now */
  20. #define FIXDS    __emit__(0x16,0x1F)/* PUSH SS, POP DS              */
  21. #define PUSH_BP  __emit__(0x55)
  22. #define POP_BP   __emit__(0x5D)
  23.  
  24. unsigned _stklen = 0x200;
  25. unsigned _heaplen = 0;
  26.  
  27. char FileName[65];  /* filename global buffer                       */
  28. char * dvrarg;      /* points to char after name in cmdline buffer  */
  29. unsigned movsize;   /* number of bytes to be moved up for driver    */
  30. void (far * driver)();  /* used as pointer to call driver code      */
  31. void far * drvptr;  /* holds pointer to device driver               */
  32. void far * nuldrvr; /* additional driver pointers                   */
  33. void far * nxtdrvr;
  34. BYTE far * nblkdrs; /* points to block device count in List of Lists*/
  35. unsigned lastdrive; /* value of LASTDRIVE in List of Lists          */
  36. BYTE far * CDSbase; /* base of Current Dir Structure                */
  37. int CDSsize;        /* size of CDS element                          */
  38. unsigned nulseg;    /* hold parts of ListOfLists pointer            */
  39. unsigned nulofs;
  40. unsigned LoLofs;
  41.  
  42. #pragma pack(1)
  43.  
  44. struct packet{      /* device driver's command packet               */
  45.     BYTE hdrlen;
  46.     BYTE unit;
  47.     BYTE command;       /* 0 to initialize      */
  48.     unsigned status;    /* 0x8000 is error      */
  49.     BYTE reserv[8];
  50.     BYTE nunits;
  51.     unsigned brkofs;    /* break adr on return  */
  52.     unsigned brkseg;    /* break seg on return  */
  53.     unsigned inpofs;    /* SI on input          */
  54.     unsigned inpseg;    /* _psp on input        */
  55.     BYTE NextDrv;       /* next available drive */
  56.   } CmdPkt;
  57.   
  58. typedef struct {    /* Current Directory Structure (CDS)            */
  59.     BYTE path[0x43];
  60.     unsigned flags;
  61.     void far *dpb;
  62.     unsigned start_cluster;
  63.     unsigned long ffff;
  64.     unsigned slash_offset;  /* offset of '\' in current path field  */
  65.     // next for DOS4+ only
  66.     BYTE unknown;
  67.     void far *ifs;
  68.     unsigned unknown2;
  69.     } CDS;
  70.  
  71. extern unsigned _psp;       /* established by startup code in c0    */
  72. extern unsigned _heaptop;   /* established by startup code in c0    */
  73. extern BYTE _osmajor;       /* established by startup code      */
  74. extern BYTE _osminor;       /* established by startup code      */
  75.  
  76. void _exit( int );          /* established by startup code in c0    */
  77. void abort( void );         /* established by startup code in c0    */
  78.  
  79. void movup( char far *, char far *, int ); /* in MOVUP.ASM file     */
  80. void copyptr( void far *src, void far *dst ); /* in MOVUP.ASM file  */
  81.  
  82. void exit(int c)            /* called by startup code's sequence    */
  83. { _exit(c);}
  84.  
  85. int Get_Driver_Name ( void )
  86. { char *nameptr;
  87.   int i, j, cmdlinesz;
  88.  
  89.   nameptr = (char *)0x80;   /* check command line for driver name   */
  90.   cmdlinesz = (unsigned)*nameptr++;
  91.   if (cmdlinesz < 1)        /* if nothing there, return FALSE       */
  92.     return 0;
  93.   for (i=0; i<cmdlinesz && nameptr[i]<'!'; i++) /* skip blanks      */
  94.     ;
  95.   dvrarg = (char *)&nameptr[i]; /* save to put in SI                */
  96.   for ( j=0; i<cmdlinesz && nameptr[i]>' '; i++)    /* copy name    */
  97.     FileName[j++] = nameptr[i];
  98.   FileName[j] = '\0';
  99.  
  100.   return 1;                 /* and return TRUE to keep going        */
  101. }
  102.  
  103. void Put_Msg ( char *msg )  /* replaces printf()                    */
  104. #ifdef INT29
  105.     /* gratuitous use of undocumented DOS */
  106.     while (*msg)
  107.     { _AL = *msg++;             /* MOV AL,*msg  */
  108.       geninterrupt(0x29);       /* INT 29h */
  109.     }
  110. #else
  111.     _AH = 2;    /* doesn't need to be inside loop */
  112.     while (*msg)
  113.     { _DL = *msg++;            
  114.       geninterrupt(0x21);
  115.     }
  116. #endif
  117. }
  118.  
  119. void Err_Halt ( char *msg )     /* print message and abort          */
  120. { Put_Msg ( msg );
  121.   Put_Msg ( "\r\n" );           /* send CR,LF   */
  122.   abort();
  123. }
  124.  
  125. void Move_Loader ( void )       /* vacate lower part of RAM         */
  126. {
  127.     unsigned movsize, destseg;
  128.     movsize = _heaptop - _psp; /* size of loader in paragraphs      */
  129.     destseg = *(unsigned far *)MK_FP( _psp, 2 ); /* end of memory   */
  130.     movup ( MK_FP( _psp, 0 ), MK_FP( destseg - movsize, 0 ),
  131.             movsize << 4);      /* move and fix segregs             */
  132. }
  133.  
  134. void Load_Drvr ( void )         /* load driver file into RAM    */
  135. { unsigned handle;
  136.   struct {
  137.     unsigned LoadSeg;
  138.     unsigned RelocSeg;
  139.   } ExecBlock;
  140.  
  141.   ExecBlock.LoadSeg = _psp + 0x10;
  142.   ExecBlock.RelocSeg = _psp + 0x10;
  143.   _DX = (unsigned)&FileName[0];
  144.   _BX = (unsigned)&ExecBlock;
  145.   _ES = _SS;                    /* es:bx point to ExecBlock     */
  146.   _AX = 0x4B03;                 /* load overlay                 */
  147.   geninterrupt ( 0x21 );        /* DS is okay on this call      */
  148.   GETFLAGS;
  149.   if ( _AH & 1 )
  150.     Err_Halt ( "Unable to load driver file." );
  151. }
  152.  
  153. void Get_List ( void )          /* set up pointers via List     */
  154. { _AH = 0x52;                   /* find DOS List of Lists       */
  155.   geninterrupt ( 0x21 );
  156.   nulseg = _ES;                 /* DOS data segment             */
  157.   LoLofs = _BX;                 /* current drive table offset   */
  158.  
  159.   switch( _osmajor )            /* NUL adr varies with version  */
  160.     {
  161.     case  0:
  162.       Err_Halt ( "Drivers not used in DOS V1." );
  163.     case  2:
  164.       nblkdrs = NULL;
  165.       nulofs = LoLofs + 0x17;
  166.       break;
  167.     case  3:
  168.       if (_osminor == 0)
  169.       {
  170.           nblkdrs = (BYTE far *) MK_FP(nulseg, LoLofs + 0x10);
  171.           lastdrive = *((BYTE far *) MK_FP(nulseg, LoLofs + 0x1b));
  172.           nulofs = LoLofs + 0x28;
  173.       }
  174.       else
  175.       {
  176.           nblkdrs = (BYTE far *) MK_FP(nulseg, LoLofs + 0x20);
  177.           lastdrive = *((BYTE far *) MK_FP(nulseg, LoLofs + 0x21));
  178.           nulofs = LoLofs + 0x22;
  179.       }
  180.       CDSbase = *(BYTE far * far *)MK_FP(nulseg, LoLofs + 0x16);
  181.       CDSsize = 81;
  182.       break;
  183.     case  4:
  184.     case  5:
  185.       nblkdrs = (BYTE far *) MK_FP(nulseg, LoLofs + 0x20);
  186.       lastdrive = *((BYTE far *) MK_FP(nulseg, LoLofs + 0x21));
  187.       nulofs  = LoLofs + 0x22;
  188.       CDSbase = *(BYTE far * far *) MK_FP(nulseg, LoLofs + 0x16);
  189.       CDSsize = 88;
  190.       break;
  191.     case 10:
  192.     case 20:
  193.       Err_Halt ( "OS2 DOS Box not supported." );
  194.     default:
  195.       Err_Halt ( "Unknown version of DOS!");
  196.     }
  197. }
  198.  
  199. void Fix_DOS_Chain ( void )     /* patches driver into DOS chn  */
  200. { unsigned i;
  201.  
  202.   nuldrvr = MK_FP( nulseg, nulofs+0x0A ); /* verify the drvr    */
  203.   drvptr = "NUL     ";
  204.   for ( i=0; i<8; ++i )
  205.     if ( *((BYTE far *)nuldrvr+i) != *((BYTE far *)drvptr+i) )
  206.       Err_Halt ( "Failed to find NUL driver." );
  207.  
  208.   nuldrvr = MK_FP( nulseg, nulofs );    /* point to NUL driver  */
  209.   drvptr  = MK_FP( _psp+0x10, 0 );      /* new driver's address */
  210.  
  211.   copyptr ( nuldrvr, &nxtdrvr );        /* hold old head now    */
  212.   copyptr ( &drvptr, nuldrvr );         /* put new after NUL    */
  213.   copyptr ( &nxtdrvr, drvptr );         /* and old after new    */
  214. }
  215.  
  216. // returns number of next free drive, -1 if none available
  217. int Next_Drive ( void )
  218. {
  219. #ifdef USE_BLKDEV
  220.   return (nblkdrs && (*nblkdrs < lastdrive)) ? *nblkdrs : -1;
  221. #else
  222.   /* The following approach takes account of SUBSTed and
  223.      network-redirector drives */
  224.   CDS far *cds;
  225.   int i;
  226.   /* find first unused entry in CDS structure */
  227.   for (i=0, cds=CDSbase; i<lastdrive; i++, ((BYTE far *)cds)+=CDSsize)
  228.     if (! cds->flags)                /* found a free drive  */
  229.         break;
  230.   return (i == lastdrive) ? -1 : i; 
  231. #endif  
  232. }
  233.  
  234. int Init_Drvr ( void )
  235. { unsigned tmp;
  236. #define INIT 0
  237.   CmdPkt.command = INIT;        /* build command packet         */
  238.   CmdPkt.hdrlen = sizeof (struct packet);
  239.   CmdPkt.unit = 0;
  240.   CmdPkt.inpofs  = (unsigned)dvrarg;    /* points into cmd line */
  241.   CmdPkt.inpseg  = _psp;
  242.   /* can't really check for next drive here, because don't yet know
  243.      if this is a block driver or not */
  244.   CmdPkt.NextDrv = Next_Drive();
  245.   drvptr  = MK_FP( _psp+0x10, 0 );      /* new driver's address */
  246.  
  247.   tmp = *((unsigned far *)drvptr+3);    /* STRATEGY pointer     */
  248.   driver = MK_FP( FP_SEG( drvptr ), tmp );
  249.   _ES = FP_SEG( (void far *)&CmdPkt );
  250.   _BX = FP_OFF( (void far *)&CmdPkt );
  251.   (*driver)();                  /* set up the packet address    */
  252.  
  253.   tmp = *((unsigned far *)drvptr+4);    /* COMMAND pointer      */
  254.   driver = MK_FP( FP_SEG( drvptr ), tmp );
  255.   (*driver)();                  /* do the initialization        */
  256.   
  257.   /* check status code in command packet                        */
  258.   return (! ( CmdPkt.status & 0x8000 ));
  259. }
  260.  
  261. int  Put_Blk_Dev ( void )   /* TRUE if Block Device failed      */
  262. { int newdrv;
  263.   int retval = 1;           /* pre-set for failure              */
  264.   int unit = 0;
  265.   BYTE far *DPBlink;
  266.   CDS far *cds;
  267.   int i;
  268.  
  269.   if ((Next_Drive() == -1) || CmdPkt.nunits == 0)
  270.     return retval;          /* cannot install block driver      */
  271.   if (CmdPkt.brkofs != 0)   /* align to next paragraph          */
  272.   {
  273.     CmdPkt.brkseg += (CmdPkt.brkofs >> 4) + 1;
  274.     CmdPkt.brkofs = 0;
  275.   }
  276.   while( CmdPkt.nunits-- )
  277.   {
  278.     if ((newdrv = Next_Drive()) == -1)
  279.         return 1;
  280.     (*nblkdrs)++;
  281.     _AH = 0x32;             /* get last DPB and set poiner      */
  282.     _DL = newdrv;
  283.     geninterrupt ( 0x21 );
  284.     _AX = _DS;              /* save segment to make the pointer */
  285.     FIXDS;
  286.     DPBlink = MK_FP(_AX, _BX);
  287.     (unsigned) DPBlink += (_osmajor < 4 ? 24 : 25 );
  288.     _SI = *(unsigned far *)MK_FP(CmdPkt.inpseg, CmdPkt.inpofs);
  289.     _ES = CmdPkt.brkseg;
  290.     _DS = CmdPkt.inpseg;
  291.     _AH = 0x53;
  292.     PUSH_BP;
  293.     _BP = 0;
  294.     geninterrupt ( 0x21 );    /* build the DPB for this unit    */
  295.     POP_BP;
  296.     FIXDS;
  297.     *(void far * far *)DPBlink = MK_FP( CmdPkt.brkseg, 0 );
  298.  
  299.     /* set up the Current Directory Structure for this drive */
  300.     cds = (CDS far *) (CDSbase + (newdrv * CDSsize));
  301.     cds->flags = 1 << 14;       /* PHYSICAL DRIVE */
  302.     cds->dpb = MK_FP(CmdPkt.brkseg, 0);
  303.     cds->start_cluster = 0xFFFF;
  304.     cds->ffff = -1L;
  305.     cds->slash_offset = 2;
  306.     if (_osmajor > 3)
  307.       { cds->unknown = 0;
  308.         cds->ifs = (void far *) 0;
  309.         cds->unknown2 = 0;
  310.       }
  311.       
  312.     /* set up pointers for DPB, driver  */
  313.     DPBlink = MK_FP( CmdPkt.brkseg, 0);
  314.     *DPBlink = newdrv;
  315.     *(DPBlink+1) = unit++;
  316.     if (_osmajor > 3)
  317.       DPBlink++;          /* add one if DOS 4                 */
  318.     *(long far *)(DPBlink+0x12) = (long)MK_FP( _psp+0x10, 0 );
  319.     *(long far *)(DPBlink+0x18) = 0xFFFFFFFF;
  320.     CmdPkt.brkseg += 2;       /* Leave two paragraphs for DPB   */
  321.     CmdPkt.inpofs += 2;       /* Point to next BPB pointer      */
  322.   }     /* end of nunits loop   */
  323.   return 0;                 /* all went okay                    */
  324. }
  325.  
  326. void Get_Out ( void )
  327. { unsigned temp;
  328.  
  329.   temp = *((unsigned far *)drvptr+2);   /* attribute word       */
  330.   if ((temp & 0x8000) == 0 )    /* if block device, set up tbls */
  331.     if (Put_Blk_Dev() )
  332.       Err_Halt( "Could not install block device" );
  333.  
  334.   Fix_DOS_Chain ();             /* else patch it into DOS       */
  335.  
  336.   _ES = *((unsigned *)MK_FP( _psp, 0x002C ));
  337.   _AH = 0x49;                   /* release environment space    */
  338.   geninterrupt ( 0x21 );
  339.  
  340.   /* then set up regs for KEEP function, and go resident        */
  341.   temp = (CmdPkt.brkofs + 15);  /* normalize the offset         */
  342.   temp >>= 4;
  343.   temp += CmdPkt.brkseg;        /* add the segment address      */
  344.   temp -= _psp;                 /* convert to paragraph count   */
  345.   _AX = 0x3100;                 /* KEEP function of DOS         */
  346.   _DX = (unsigned)temp;         /* paragraphs to retain         */
  347.   geninterrupt ( 0x21 );        /* won't come back from here!   */
  348. }
  349.  
  350. void main ( void )
  351. { if (!Get_Driver_Name() )
  352.     Err_Halt ( "Device driver name required.");
  353.   Move_Loader ();               /* move code high and jump      */
  354.   Load_Drvr ();                 /* bring driver into freed RAM  */
  355.   Get_List();                   /* get DOS internal variables   */
  356.   if (Init_Drvr ())             /* let driver do its thing      */
  357.       Get_Out();                /* check init status, go TSR    */
  358.   else
  359.       Err_Halt ( "Driver initialization failed." );
  360. }
  361.  
  362.  
  363. [LISTING TWO]
  364.  
  365.         NAME    movup
  366. ;[]------------------------------------------------------------[]
  367. ;|      MOVUP.ASM -- helper code for DEVLOD.C                   |
  368. ;|      Copyright 1990 by Jim Kyle - All Rights Reserved        |
  369. ;[]------------------------------------------------------------[]
  370.  
  371. _TEXT   SEGMENT BYTE PUBLIC 'CODE'
  372. _TEXT   ENDS
  373.  
  374. _DATA   SEGMENT WORD PUBLIC 'DATA'
  375. _DATA   ENDS
  376.  
  377. _BSS    SEGMENT WORD PUBLIC 'BSS'
  378. _BSS    ENDS
  379.  
  380. DGROUP  GROUP   _TEXT, _DATA, _BSS
  381.  
  382. ASSUME  CS:_TEXT, DS:DGROUP
  383.  
  384. _TEXT   SEGMENT BYTE PUBLIC 'CODE'
  385.  
  386. ;-----------------------------------------------------------------
  387. ;       movup( src, dst, nbytes )
  388. ;       src and dst are far pointers. area overlap is NOT okay
  389. ;-----------------------------------------------------------------
  390.         PUBLIC  _movup
  391.  
  392. _movup  PROC      NEAR
  393.         push    bp
  394.         mov     bp, sp
  395.         push    si
  396.         push    di
  397.         lds     si,[bp+4]               ; source
  398.         les     di,[bp+8]               ; destination
  399.         mov     bx,es                   ; save dest segment
  400.         mov     cx,[bp+12]              ; byte count
  401.         cld
  402.         rep     movsb                   ; move everything to high ram
  403.         mov     ss,bx                   ; fix stack segment ASAP
  404.         mov     ds,bx                   ; adjust DS too
  405.         pop     di
  406.         pop     si
  407.         mov     sp, bp
  408.         pop     bp
  409.         pop     dx                      ; Get return address
  410.         push    bx                      ; Put segment up first
  411.         push    dx                      ; Now a far address on stack
  412.         retf
  413. _movup  ENDP
  414.  
  415. ;-------------------------------------------------------------------
  416. ;       copyptr( src, dst )
  417. ;       src and dst are far pointers.
  418. ;       moves exactly 4 bytes from src to dst.
  419. ;-------------------------------------------------------------------
  420.         PUBLIC  _copyptr
  421.  
  422. _copyptr        PROC      NEAR
  423.         push    bp
  424.         mov     bp, sp
  425.         push    si
  426.         push    di
  427.         push    ds
  428.         lds     si,[bp+4]               ; source
  429.         les     di,[bp+8]               ; destination
  430.         cld
  431.         movsw
  432.         movsw
  433.         pop     ds
  434.         pop     di
  435.         pop     si
  436.         mov     sp, bp
  437.         pop     bp
  438.         ret
  439. _copyptr        ENDP
  440.  
  441. _TEXT   ENDS
  442.  
  443.         end
  444.  
  445.  
  446. [LISTING THREE]
  447.  
  448.  
  449.  
  450.         NAME    c0
  451. ;[]------------------------------------------------------------[]
  452. ;|      C0.ASM -- Start Up Code                                 |
  453. ;|        based on Turbo-C startup code, extensively modified   |
  454. ;[]------------------------------------------------------------[]
  455.  
  456. _TEXT   SEGMENT BYTE PUBLIC 'CODE'
  457. _TEXT   ENDS
  458.  
  459. _DATA   SEGMENT WORD PUBLIC 'DATA'
  460. _DATA   ENDS
  461.  
  462. _BSS    SEGMENT WORD PUBLIC 'BSS'
  463. _BSS    ENDS
  464.  
  465. DGROUP  GROUP   _TEXT, _DATA, _BSS
  466.  
  467. ;       External References
  468.  
  469. EXTRN   _main : NEAR
  470. EXTRN   _exit : NEAR
  471.  
  472. EXTRN   __stklen : WORD
  473. EXTRN   __heaplen : WORD
  474.  
  475. PSPHigh         equ     00002h
  476. PSPEnv          equ     0002ch
  477.  
  478. MINSTACK        equ     128     ; minimal stack size in words
  479.  
  480. ;       At the start, DS, ES, and SS are all equal to CS
  481.  
  482. ;/*-----------------------------------------------------*/
  483. ;/*     Start Up Code                                   */
  484. ;/*-----------------------------------------------------*/
  485.  
  486. _TEXT   SEGMENT BYTE PUBLIC 'CODE'
  487.  
  488. ASSUME  CS:_TEXT, DS:DGROUP
  489.  
  490.         ORG     100h
  491.  
  492. STARTX  PROC    NEAR
  493.  
  494.         mov     dx, cs          ; DX = GROUP Segment address
  495.         mov     DGROUP@, dx
  496.         mov     ah, 30h         ; get DOS version
  497.         int     21h
  498.         mov     bp, ds:[PSPHigh]; BP = Highest Memory Segment Addr
  499.         mov     word ptr __heaptop, bp
  500.         mov     bx, ds:[PSPEnv] ; BX = Environment Segment address
  501.         mov     __version, ax   ; Keep major and minor version number
  502.         mov     __psp, es       ; Keep Program Segment Prefix address
  503.  
  504. ;       Determine the amount of memory that we need to keep
  505.  
  506.         mov     dx, ds          ; DX = GROUP Segment address
  507.         sub     bp, dx          ; BP = remaining size in paragraphs
  508.         mov     di, __stklen    ; DI = Requested stack size
  509. ;
  510. ; Make sure that the requested stack size is at least MINSTACK words.
  511. ;
  512.         cmp     di, 2*MINSTACK  ; requested stack big enough ?
  513.         jae     AskedStackOK    ; yes, use it
  514.         mov     di, 2*MINSTACK  ; no,  use minimal value
  515.         mov     __stklen, di    ; override requested stack size
  516. AskedStackOK:
  517.         add     di, offset DGROUP: edata
  518.         jb      InitFailed      ; DATA segment can NOT be > 64 Kbytes
  519.         add     di, __heaplen
  520.         jb      InitFailed      ; DATA segment can NOT be > 64 Kbytes
  521.         mov     cl, 4
  522.         shr     di, cl          ; $$$ Do not destroy CL $$$
  523.         inc     di              ; DI = DS size in paragraphs
  524.         cmp     bp, di
  525.         jnb     TooMuchRAM      ; Enough to run the program
  526.  
  527. ;       All initialization errors arrive here
  528.  
  529. InitFailed:
  530.         jmp     near ptr _abort
  531.  
  532. ;       Set heap base and pointer
  533.  
  534. TooMuchRAM:
  535.         mov     bx, di          ; BX = total paragraphs in DGROUP
  536.         shl     di, cl          ; $$$ CX is still equal to 4 $$$
  537.         add     bx, dx          ; BX = seg adr past DGROUP
  538.         mov     __heapbase, bx
  539.         mov     __brklvl, bx
  540. ;
  541. ;       Set the program stack down into RAM that will be kept.
  542. ;
  543.         cli
  544.         mov     ss, dx          ; DGROUP
  545.         mov     sp, di          ; top of (reduced) program area
  546.         sti
  547.  
  548.         mov     bx,__heaplen    ; set up heap top pointer
  549.         add     bx,15
  550.         shr     bx,cl           ; length in paragraphs
  551.         add     bx,__heapbase
  552.         mov     __heaptop, bx
  553. ;
  554. ;       Clear uninitialized data area to zeroes
  555. ;
  556.         xor     ax, ax
  557.         mov     es, cs:DGROUP@
  558.         mov     di, offset DGROUP: bdata
  559.         mov     cx, offset DGROUP: edata
  560.         sub     cx, di
  561.         rep     stosb
  562. ;
  563. ;       exit(main());
  564. ;
  565.         call    _main           ; the real C program
  566.         push    ax
  567.         call    _exit           ; part of the C program too
  568.  
  569. ;----------------------------------------------------------------
  570. ;       _exit()
  571. ;       Restore interrupt vector taken during startup.
  572. ;       Exit to DOS.
  573. ;----------------------------------------------------------------
  574.  
  575.         PUBLIC  __exit
  576. __exit  PROC      NEAR
  577.         push    ss
  578.         pop     ds
  579.  
  580. ;       Exit to DOS
  581.  
  582. ExitToDOS:
  583.         mov     bp,sp
  584.         mov     ah,4Ch
  585.         mov     al,[bp+2]
  586.         int     21h                     ; Exit to DOS
  587.  
  588. __exit  ENDP
  589.  
  590. STARTX  ENDP
  591.  
  592. ;[]------------------------------------------------------------[]
  593. ;|      Miscellaneous functions                                 |
  594. ;[]------------------------------------------------------------[]
  595.  
  596. ErrorDisplay    PROC    NEAR
  597.                 mov     ah, 040h
  598.                 mov     bx, 2           ; stderr device
  599.                 int     021h
  600.                 ret
  601. ErrorDisplay    ENDP
  602.  
  603.         PUBLIC  _abort
  604. _abort  PROC      NEAR
  605.                 mov     cx, lgth_abortMSG
  606.                 mov     dx, offset DGROUP: abortMSG
  607. MsgExit3        label   near
  608.                 push    ss
  609.                 pop     ds
  610.                 call    ErrorDisplay
  611. CallExit3       label   near
  612.                 mov     ax, 3
  613.                 push    ax
  614.                 call    __exit          ; _exit(3);
  615. _abort   ENDP
  616.  
  617. ;       The DGROUP@ variable is used to reload DS with DGROUP
  618.  
  619.         PUBLIC  DGROUP@
  620. DGROUP@     dw    ?
  621.  
  622. _TEXT   ENDS
  623.  
  624. ;[]------------------------------------------------------------[]
  625. ;|      Start Up Data Area                                      |
  626. ;[]------------------------------------------------------------[]
  627.  
  628. _DATA   SEGMENT WORD PUBLIC 'DATA'
  629.  
  630. abortMSG        db      'Quitting program...', 13, 10
  631. lgth_abortMSG   equ     $ - abortMSG
  632.  
  633. ;
  634. ;                       Miscellaneous variables
  635. ;
  636.         PUBLIC  __psp
  637.         PUBLIC  __version
  638.         PUBLIC  __osmajor
  639.         PUBLIC  __osminor
  640.  
  641. __psp           dw      0
  642. __version       label   word
  643. __osmajor       db      0
  644. __osminor       db      0
  645.  
  646. ;       Memory management variables
  647.  
  648.         PUBLIC  ___heapbase
  649.         PUBLIC  ___brklvl
  650.         PUBLIC  ___heaptop
  651.         PUBLIC  __heapbase
  652.         PUBLIC  __brklvl
  653.         PUBLIC  __heaptop
  654.  
  655. ___heapbase     dw      DGROUP:edata
  656. ___brklvl       dw      DGROUP:edata
  657. ___heaptop      dw      DGROUP:edata
  658. __heapbase      dw      0
  659. __brklvl        dw      0
  660. __heaptop       dw      0
  661.  
  662. _DATA   ENDS
  663.  
  664. _BSS    SEGMENT WORD PUBLIC 'BSS'
  665.  
  666. bdata   label   byte
  667. edata   label   byte            ; mark top of used area
  668.  
  669. _BSS    ENDS
  670.  
  671.         END     STARTX
  672.  
  673.  
  674.  
  675. [LISTING FOUR]
  676.  
  677. # makefile for DEVLOD.COM 
  678. # can substitute other assemblers for TASM
  679.  
  680. c0.obj  :       c0.asm
  681.         tasm c0 /t/mx/la;
  682.  
  683. movup.obj:      movup.asm
  684.         tasm movup /t/mx/la;
  685.  
  686. devlod.obj:     devlod.c
  687.         tcc -c -ms devlod
  688.  
  689. devlod.com:     devlod.obj c0.obj movup.obj
  690.         tlink c0 movup devlod /c/m,devlod
  691.         if exist devlod.com del devlod.com
  692.         exe2bin devlod.exe devlod.com
  693.         del devlod.exe
  694.  
  695.