home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / Samples / CASM.ARJ / EXEC2.ASM < prev    next >
Assembly Source File  |  1988-07-27  |  14KB  |  571 lines

  1. ;_ exec2.asm   Fri Feb 19 1988   Modified by: Walter Bright */
  2. ; Copyright (C) 1985-1988 by Northwest Software
  3. ; All Rights Reserved
  4.  
  5. include    macros.asm
  6.  
  7.     begdata
  8.  
  9.     c_extrn    _doserrno,word, _psp,word
  10.  
  11. header    equ    $            ; .EXE file header
  12. fcb1    db    37 dup (?)
  13. fcb2    db    37 dup (?)        ; a sub-process may want these
  14.  
  15. param_block    equ $
  16. env    dw    ?            ; segment address of environment
  17. comml    dw    ?            ; DWORD ptr to command line
  18.     dw    ?            ; segment of command line
  19. pfcb1    dw    offset DGROUP:fcb1    ; DWORD points to first FCB
  20.     dw    ?            ; segment of first FCB
  21. pfcb2    dw    offset DGROUP:fcb2    ; DWORD ptr to second FCB
  22.     dw    ?            ; segment of second FCB
  23.  
  24.     enddata
  25.  
  26.     begcode    exec2
  27.  
  28. ; Must store these in code segment so we can find them after the exec
  29. oldsp    dw    ?            ; save SP
  30. oldss    dw    ?            ; save SS
  31.  
  32. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  33. ; Execute a command.
  34. ; Use:
  35. ;    int _exec(command,commandline,envseg,chain)
  36. ;    char *command;        /* name of program to run          */
  37. ;    char *commandline;    /* command line (128 bytes max preceeded  */
  38. ;                /* by a byte count and ended with a \r)      */
  39. ;                /* (an extra 0 at the end is recommended) */
  40. ;    int envseg;        /* segment of environment          */
  41. ;    int chain;        /* if !=0, then 'chain' to program, and      */
  42. ;                /* terminate program when command terminates */
  43. ;
  44. ; Returns:
  45. ;    -1    error (_doserrno has the MS-DOS error code)
  46. ;    n    success (n is the exit status of the executed command)
  47.  
  48.  
  49.     c_public    _exec
  50. func    _exec
  51.     push    BP            ; save old stack frame
  52.     mov    BP,SP
  53.     .save    <SI,DI>
  54.     IF LPTR
  55.     push    DS            ; save DS
  56.     ENDIF
  57.     mov    AX,P+SIZEPTR+SIZEPTR[BP] ; get segment of environment
  58.     mov    env,AX
  59.     mov    CS:oldsp,SP
  60.     mov    CS:oldss,SS        ; only thing preserved by exec is
  61.                     ; CS and IP, so we must save these
  62.                     ; in the code segment
  63.     setESeqDS            ; ES -> data segment
  64.     IF SPTR
  65.     mov    SI,P+2[BP]        ; SI = commandline
  66.     mov    comml,SI        ; offset of command line
  67.     mov    comml+2,DS        ; segment of command line
  68.     ELSE
  69.     lds    SI,P+4[BP]        ; DS:SI = commandline
  70.     segES
  71.     mov    DGROUP:comml,SI        ; offset of command line
  72.     segES
  73.     mov    DGROUP:comml+2,DS    ; segment of command line
  74.     ENDIF
  75.     cld
  76.     lodsb                ; AL = # of chars in command line
  77.     tst    AL
  78.     jz    L2            ; no chars in command line
  79.                     ; DS:SI -> command line
  80.     mov    DI,offset DGROUP:fcb1    ; ES:DI -> fcb1
  81.     mov    AX,2901h
  82.     bdos                ; parse filename into fcb1
  83.     mov    DI,offset DGROUP:fcb2    ; ES:DI -> fcb2
  84.     mov    AX,2901h
  85.     bdos                ; parse second filename into fcb2
  86. L2:
  87.     .if    <word ptr P+SIZEPTR+SIZEPTR+2[BP]> ne 0, L4 ; if chain to next program
  88.     IF SPTR
  89.     mov    pfcb1+2, ES
  90.     mov    pfcb2+2, ES        ; segment values
  91.     mov    DX,P[BP]        ; DS:DX -> command
  92.     ELSE
  93.     segES
  94.     mov    DGROUP:pfcb1+2, ES
  95.     segES
  96.     mov    DGROUP:pfcb2+2, ES    ; segment values
  97.     lds    DX,P[BP]        ; DS:DX -> command
  98.     ENDIF
  99.     mov    BX,offset DGROUP:param_block    ; ES:BX -> parameter block
  100.     mov    AX,4B00h        ; load/execute program
  101.     bdos                ; perform exec
  102. L6:    cld                ; no direction flag bugs
  103.  
  104.     mov    BX,CS:oldss
  105.     cli                ;for bug in old 8088's
  106.     mov    SS,BX            ; restore SS
  107.     mov    SP,CS:oldsp        ; restore SP
  108.     sti                ;for bug in old 8088's
  109.     IF SPTR
  110.     mov    DS,BX
  111.     mov    ES,BX            ; restore DS,ES
  112.     ENDIF
  113.     IF LPTR
  114.     pop    DS            ; restore DS
  115.     ENDIF
  116.     jc    L1            ; if (error)
  117.  
  118.     bdos    4Dh            ; get exit status of sub-process
  119. L3:    .restore <DI,SI>
  120.     pop    BP
  121.     ret
  122.  
  123. L1:    mov    _doserrno,AX        ;    save error code in _doserrno
  124.     sbb    AX,AX            ;     return (-1)
  125.     jmp    L3
  126.  
  127. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  128. ;Chain to command
  129. L4:
  130.     ;At this point:
  131.     ;ES -> data segment
  132.     ;DS -> segment of command line
  133.     ;Direction flag = forward
  134.  
  135.     ;From now on, we can't fix things as they were before the
  136.     ;call to _exec(). Therefore, all errors simply abort back
  137.     ;to DOS with an errorlevel of 1.
  138.  
  139.     ;Restore vectors 0x22, 0x23 and 0x24 from PSP
  140.     segES
  141.     mov    AX,DGROUP:_psp
  142.     push    ES
  143.     mov    ES,AX
  144.     mov    CX,3        ;3 vectors to restore
  145.     mov    BX,0Ah
  146.     mov    AX,2522h    ;DOS function 25, vector 22
  147. L8:    mov    DX,ES:[BX]
  148.     mov    DS,ES:2[BX]
  149.     bdos            ;set interrupt vector
  150.     add    BX,2
  151.     inc    AL
  152.     loop    L8
  153.     pop    ES
  154.  
  155.     IF 0
  156. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  157. ;Use our own loader
  158.  
  159.     ;Rewrite the PSP of our own program, we will cause the
  160.     ;exec'ed program to sit right on top of ours
  161.  
  162.     push    ES
  163.     pop    DS            ;now DS -> data segment
  164.  
  165.     ;Load values into the PSP
  166.     mov    ES,_psp
  167.     mov    DI,5Ch            ;ES:DI -> where first FCB will go
  168.     mov    CX,(6Ch - 5Ch)/2    ;size of FCB area in words
  169.     mov    SI,offset DGROUP:fcb1
  170.     rep    movsw            ;transfer first FCB
  171.     mov    CX,(6Ch - 5Ch)/2    ;size of FCB area in words
  172.     mov    SI,offset DGROUP:fcb2
  173.     rep    movsw            ;transfer second FCB
  174.     mov    DI,80h            ;offset of command line
  175.     mov    CX,128/2        ;128 bytes max in command line
  176.     if SPTR
  177.     mov    SI,P+SIZEPTR[BP]
  178.     rep    movsw            ;transfer command line to PSP
  179.     else
  180.     push    DS
  181.     lds    SI,P+SIZEPTR[BP]
  182.     rep    movsw            ;transfer command line to PSP
  183.     pop    DS
  184.     endif
  185.  
  186.     ;Attempt to grow our segment as large as possible.
  187.     ;Store new top of memory back into PSP
  188.     mov    BX,0FFFFh
  189.     bdos    4Ah
  190.     jnc    E1            ;succeeded
  191.     bdos                ;try again with max allowable size
  192.     jc    err1            ;error (probably corrupted memory)
  193. E1:    mov    SI,ES
  194.     add    SI,BX
  195.     mov    ES:2,SI            ;new top of memory in paragraphs
  196.     mov    DI,BX
  197.  
  198.     ;Open the executable file
  199.     IF SPTR
  200.     mov    DX,P[BP]
  201.     bdos    3Dh
  202.     ELSE
  203.     push    DS
  204.     lds    DX,P[BP]
  205.     bdos    3Dh
  206.     pop    BP
  207.     ENDIF
  208. err1:    jc    error
  209.  
  210.     ;Read the first 28 bytes of the file. If a .EXE file, this
  211.     ;will be the header
  212.     mov    DX,offset DGROUP:header
  213.     mov    CX,28
  214.     bdos    3Fh
  215.     jc    error
  216.  
  217.     ;If the first two bytes are the EXE signature, assume we have
  218.     ;an EXE file.
  219.     .if    <word ptr DS:header[0]> e 05A4Dh, dotexe
  220.  
  221.     ;Assume it's a .COM file.
  222.     ;Rewind the file back to the beginning, as there is no header
  223.     ;for a .COM file.
  224.     clr    CX
  225.     clr    DX
  226.     mov    AX,04200
  227.     bdos
  228.     jc    error
  229.  
  230.     ;At this point:
  231.     ;ES -> PSP
  232.     ;DS -> data segment
  233.     ;BX = .COM file handle
  234.     ;SI = top paragraph of available memory
  235.     ;DI = # of paragraphs available
  236.  
  237.     ;Compute into BP the offset of the top of memory for the .COM
  238.     ;program (it gets chopped off at 64k)
  239.     sub    SI,8
  240.     sub    DI,8        ;allow 128 bytes for bootstrap + boot stack
  241.     mov    BP,1000h
  242.     .if    DI a BP, C2
  243.     mov    BP,DI
  244. C2:    mov    CL,4
  245.     shl    BP,CL        ;*16 to get from paragraphs to offset
  246.     mov    ES:6,BP        ;store # of bytes in segment in PSP
  247.  
  248.     ;We'll need a bootstrap routine and some stack that won't be
  249.     ;overwritten when the .COM file is read in. Do this by
  250.     ;blitting a boot program to the end of memory.
  251.     mov    AX,ES            ;save PSP segment for later
  252.     mov    ES,SI
  253.     cli                ;for bug in old 8088's
  254.     mov    SS,SI
  255.     mov    SP,8*16            ;set stack to end
  256.     sti                ;for bug in old 8088's
  257.     clr    DI            ;ES:DI -> relocation address
  258.  
  259.     push    ES
  260.     push    DI            ;put relocation addr on stack for retf
  261.  
  262.     push    CS
  263.     pop    DS
  264.     mov    SI,offset $comstart    ;DS:SI -> start of bootstrap
  265.     mov    CX,offset $comend - offset $comstart
  266.     rep    movsb
  267.  
  268.     ;Compute into CX the max number of bytes that we can read in
  269.     mov    CX,BP
  270.     mov    DX,0100h    ;offset of start of .COM program
  271.     sub    CX,DX        ;.COM files can't be larger than
  272.                 ;64k - sizeof(PSP)
  273.     inc    CX        ;1 more byte to see if file is larger than
  274.                 ;will fit in memory
  275.     ;Do the actual read
  276.     mov    ES,AX
  277.     mov    DS,AX        ;DS = segment of PSP
  278.     .retf            ;continue $comstart
  279.  
  280. $comstart:
  281.     bdos    3Fh        ;read
  282.     jc    error
  283.     .if    AX e CX, error    ;read too much, file is too big
  284.     bdos    3Eh        ;close file
  285.     jc    error
  286.  
  287.     ;Initialize registers for a .COM program    
  288.     mov    AX,DS
  289.     cli                ;for bug in old 8088's
  290.     mov    SS,AX
  291.     mov    SP,BP        ;stack is at end of segment
  292.     sti                ;for bug in old 8088's
  293.  
  294.     clr    AX
  295.     push    AX        ;leave word of 0s on top of stack
  296.     push    DS
  297.     push    DX
  298.     .retf            ;start execution
  299.  
  300. error:    mov    AX,04C01h    ;return to DOS with error
  301.     bdos
  302. $comend:
  303.  
  304. ; .EXE file loader
  305. dotexe:
  306.     ;At this point:
  307.     ;ES -> PSP
  308.     ;DS -> data segment
  309.     ;BX = .COM file handle
  310.     ;SI = top paragraph of available memory
  311.     ;DI = # of paragraphs available
  312.  
  313.     ;Seek to the start of the load module
  314.     mov    DX,word ptr DS:header[8] ;offset in paragraphs to start of load module
  315.     clr    AX
  316.     mov    CX,4
  317. E2:    shl    DX,1
  318.     rcl    AX,1
  319.     loop    E2
  320.     mov    CX,AX        ;CX,DX = offset of start of load module
  321.     mov    AX,04200h
  322.     bdos
  323.     jc    error
  324.  
  325.     ;Adjust available memory downwards by amount we need
  326.     ;for the header plus bootstrap loader plus stack
  327.     sub    SI,512/16
  328.     sub    DI,512/16
  329.     mov    DI,BP        ;save # of paragraphs available
  330.  
  331.     cli                ;for bug in old 8088's
  332.     mov    SS,SI
  333.     mov    SP,512/16    ;set stack to top of new area
  334.     sti                ;for bug in old 8088's
  335.  
  336.     ;Transfer header information to new area
  337.     mov    DX,ES                ;DX = segment of PSP
  338.     mov    ES,SI
  339.     mov    SI,offset DGROUP:header        ;DS:SI -> header in data segment
  340.     clr    DI                ;ES:DI -> relocated header
  341.     mov    CX,(28-2)/2    ;sizeof(header) - (unnecessary stuff)
  342.     rep    movsw        ;effect the transfer
  343.  
  344.     ;Transfer bootstrap loader to new area
  345.     mov    SI,offset $exestart
  346.     push    CS
  347.     pop    DS            ;DS:SI -> original code
  348.     mov    AX,ES            ;save segment of relocated code
  349.     push    ES
  350.     push    DI            ;so we can retf to relocated code
  351.     mov    CX,offset $exeend - $exestart    ;# of bytes to relocate
  352.     rep    movsw                ;relocate
  353.  
  354.     ;Determine size of load module
  355.     mov    ES,AX            ;ES -> segment of relocated code
  356.     mov    SI,ES:4            ;size of file in 512 byte increments
  357.     mov    CL,5
  358.     shl    SI,CL            ;size of file in paragraphs
  359.     sub    SI,ES:8            ;AX = size of load module in paragraphs
  360.     sub    BP,100h/16        ;reserve room for PSP
  361.     .if    SI be BP, E8        ;if file will fit in memory
  362.     jmp    error            ;too big
  363.  
  364. E8:    mov    BP,DX            ;BP = segment of PSP
  365.     mov    DS,DX            ;DS = segment of PSP
  366.     mov    DX,100h            ;start loading at offset 100
  367.     .retf                ;jmp to $exestart
  368.  
  369. $exestart:
  370. E3:    mov    AX,0F000h/16        ;load big chunks at a time
  371.     .if    SI ae AX, E4        ;if not too much
  372.     mov    AX,SI            ;read exactly enough
  373. E4:    mov    CL,4
  374.     shl    AX,CL
  375.     mov    CX,AX            ;CX = # of bytes to read
  376.     bdos    3Fh
  377.     jc    exeerror
  378.     tst    AX            ;done loading file?
  379.     jz    E5            ;yes
  380.     mov    AX,DS
  381.     add    AX,0F000h/16
  382.     mov    DS,AX            ;segment of where next chunk is going
  383.     sub    SI,0F000h/16
  384.     ja    E3            ;more to load
  385. E5:
  386.  
  387.     ;Start on the relocation items
  388.     push    CS
  389.     pop    DS            ;DS:0 is the header
  390.  
  391.     ;Seek to where the relocation items are
  392.     clr    CX
  393.     mov    DX,DS:18h        ;offset of first relocation item
  394.     mov    AX,4200h
  395.     bdos
  396.     jc    exeerror
  397.  
  398.     .if    <word ptr DS:6> e 0, E6    ;if no relocation items
  399.     clr    DX            ;load relocation info into header
  400.     mov    CX,4            ;one relocation item is 4 bytes
  401. E7:    bdos    3Fh            ;read
  402.     jc    exeerror
  403.     .if    AX ne CX, exeerror
  404.     mov    AX,BP
  405.     mov    DI,DS:0            ;DI = relocation offset
  406.     add    AX,DS:2            ;AX = start segment + relocation segment
  407.     mov    ES,AX
  408.     add    ES:[DI],BP        ;relocate with start segment value
  409.     dec    word ptr DS:6        ;relocation item count
  410.     jne    E7
  411.  
  412. E6:    bdos    3Eh            ;close .EXE file
  413.     jc    exeerror
  414.  
  415.     add    DS:16h,BP        ;relocate code segment
  416.     add    DS:0Eh,BP        ;relocate stack segment
  417.     cli                ;for bug in old 8088's
  418.     mov    SS,DS:0Eh
  419.     mov    SP,DS:10h        ;stack of target executable
  420.     sti                ;for bug in old 8088's
  421.     sub    BP,100h/16        ;BP = segment of PSP
  422.     mov    DS,BP
  423.     mov    ES,BP            ;DS and ES point to PSP
  424.     jmp    CS:dword ptr DS:14h    ;jump to program
  425.  
  426. exeerror:
  427.     mov    AX,04C01h    ;return to DOS with error
  428.     bdos
  429. $exeend:
  430. page
  431.     ELSE
  432. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  433. ;Use MS-DOS's exec function
  434.  
  435.     ;transfer routine to lowest address in program
  436.     push    ES        ;save data segment value
  437.     mov    CX,offset $end - offset $start        ;# of bytes to move
  438.     segES
  439.     mov    AX,DGROUP:_psp    ;AX = segment of _PSP
  440.  
  441.     if LCODE
  442.     ;Starting code segment is 256 bytes past _psp
  443.     add    AX,10h            ;+= 256 in paragraphs
  444.     mov    ES,AX
  445.     clr    DI            ;ES:DI = start of code
  446.     else
  447.     mov    BX,CS
  448.     push    CS            ;only one code segment, and CS is it
  449.     pop    ES
  450.     mov    DI,100h            ;starting offset for COM files
  451.     .if    AX e BX, EXEC1        ;COM file if (_PSP == CS)
  452.     clr    DI            ;EXE file, offset is 0
  453. EXEC1:                    ;ES:DI = destination
  454.     endif
  455.  
  456.     ;At this point, ES:DI -> start of code (just after _PSP)
  457.  
  458.     push    CS
  459.     pop    DS
  460.     mov    SI,offset $start    ;DS:SI = source
  461.  
  462.     cld
  463.     rep    movsb            ;transfer
  464.     pop    DS            ;DS = regular data segment value
  465.  
  466.     ;Transfer command tail to just after the routine
  467.     mov    comml,DI
  468.     mov    comml+2,ES        ;new segment and offset of tail
  469.     if SPTR
  470.     mov    SI,P+SIZEPTR[BP]
  471.     else
  472.     push    DS
  473.     lds    SI,P+SIZEPTR[BP]    ;DS:SI -> command tail
  474.     endif
  475.     mov    CL,[SI]
  476.     clr    CH
  477.     add    CX,3            ;allow for count and \r and 0 at end
  478.     rep    movsb
  479.     if LPTR
  480.     pop    DS
  481.     endif
  482.  
  483.     ;Transfer fcbs to just after the command tail
  484.     ;At this point:
  485.     ;    ES = segment of first code segment after PSP
  486.     ;    DI = offset into that
  487.     ;    DS = regular data segment
  488.     mov    pfcb1,DI
  489.     mov    pfcb2,DI
  490.     add    pfcb2,37
  491.     mov    pfcb1+2,ES
  492.     mov    pfcb2+2,ES
  493.     mov    SI,offset DGROUP:fcb1
  494.     mov    CX,37+37+(7*2)
  495.     rep    movsb
  496.  
  497.     mov    AX,_psp            ;get it before DS changes
  498.  
  499.     ;Get pointer to command before we move the stack
  500.     if SPTR
  501.     mov    DX,P[BP]        ; DS:DX -> command
  502.     else
  503.     lds    DX,P[BP]        ; DS:DX -> command
  504.     endif
  505.  
  506.     ;Create a stack of 128 bytes right after this
  507.     mov    SI,DI
  508.     add    DI,128 + 1        ;stack + round
  509.     and    DI,0FFFEh        ;round DI up to next word
  510.     mov    BX,ES
  511.     cli                ;for bug in old 8088's
  512.     mov    SS,BX
  513.     mov    SP,DI
  514.     sti                ;for bug in old 8088's
  515.  
  516.     push    ES            ;save segment of parameter block
  517.     mov    ES,AX            ;segment of block to be modified (_psp)
  518.     mov    BX,DI
  519.     add    BX,100h+15        ;# of bytes in PSP + round up
  520.                     ;(assume that seg $progstart is
  521.                     ; just past the PSP)
  522.     shr    BX,1
  523.     shr    BX,1
  524.     shr    BX,1
  525.     shr    BX,1            ;convert to paragraphs
  526. ;public $test
  527. $test:
  528.     if LCODE
  529.     push    AX
  530.     mov    AX,100h
  531.     push    AX
  532.     .retf
  533.     else
  534.     mov    CX,CS
  535.     .if    AX ne CX, EXEC2
  536.     mov    AX,100h            ;.COM program
  537.     push    AX
  538.     ret
  539.  
  540. EXEC2:    clr    AX            ;.EXE program
  541.     push    AX
  542.     ret
  543.     endif
  544. ;    jmp    $progstart
  545.  
  546. $start:
  547.     ;modify allocated block
  548.     bdos    4Ah            ;reset size of this program to ES:BX
  549.  
  550.     mov    BX,SI
  551.     sub    BX,7*2            ; BX = offset of parameter block
  552.     pop    ES            ; ES:BX -> parameter block
  553.     mov    AX,4B00h        ;load and execute program
  554.     bdos
  555.     jnc    L5            ;if no error
  556.  
  557.     mov    AL,1            ;exit code for failure
  558.     jmps    L7
  559.  
  560. L5:    bdos    4Dh            ;get return code
  561. L7:    bdos    4Ch            ;terminate with return code
  562. $end:
  563.  
  564.     ENDIF
  565.  
  566. c_endp    _exec
  567.  
  568.     endcode    exec2
  569.  
  570.     end
  571.