Search code examples
assemblydosx86-16fasm

How to execute DOS' COMMAND.COM command from FASM?


Is there any DOS function to execute string, containing command.com's line? I need something like C's system(), but for DOS and FASM.


Solution

  • I've previously used code like this to execute DOS shell commands. It's for nasm, but you might be able to adapt it for your purposes. This code specifically executes our own command line tail as a DOS command, but you can patch in some other command line tail if you want to execute a different command.

    section .data
    comspec db      "COMSPEC="      ; for finding COMSPEC in the env block
    comlen  equ     $-comspec
    command db      "COMMAND.COM",0
    
            ; EXEC parameter block
    execpar dw      0               ; environment for child (use ours)
            dw      80h, 0h         ; command tail (use ours)
            dw      5Ch, 0h         ; first FCB (use ours)
            dw      6Ch, 0h         ; second FCB (use ours)
    
    section .text
            ; execute DOS command
    doexec:
            mov     bx, execpar     ; EXEC parameter block
            mov     [bx+4], cs      ; fix up segment for command tail
            mov     [bx+8], cs      ; fix up segment for first FCB
            mov     [bx+12], cs     ; fix up segment for second FCB
    
            call    fndcom          ; write pointer to COMSPEC value to DS:SI
            mov     dx, si
            mov     ax, 4B00h       ; LOAD AND EXECUTE PROGRAM
            int     21h
            jnc     .ok             ; error occured?
    
            push    ax              ; remember error code
            call    fndcom          ; find COMSPEC value anew
            pop     ax              ; restore error code
            call    perror          ; print error message
    
    .ok:    push    cs              ; restore ds
            pop     ds
    
            ret
    
            ; find COMSPEC in the environment block
            ; and load pointer to it to DS:SI
            ; preserves bx
    fndcom: mov     ds, [2Ch]       ; environment block
            xor     si, si          ; beginning of env block
    
            ; loop invariant: si points to the beginning of
            ; a string in the environment
    .loop1: cmp     byte [si], 0    ; end of environment reached?
            je      .nope
    
            mov     di, comspec     ; find "COMSPEC="
            mov     cx, comlen      ;
            repe    cmpsb           ; compare strings
            je      .found          ; if found, we are done
    
            dec     si              ; go back to mismatched character
    .loop2: lodsb                   ; search si for end of string
            test    al, al          ; end of string reached?
            jne     .loop2          ; nope
    
            jmp     .loop1          ; check next string in environment
    
            ; COMSPEC unset
    .nope:  push    cs
            pop     ds              ; restore ds
            mov     si, command     ; "COMMAND.COM"
            ret
    
            ; COMSPEC found
    .found: ret
    

    The basic idea is to find the name of the command interpreter by searching for a variable named COMSPEC in the environment block. If none is found, we default to COMMAND.COM. Then, we build an exec parameter block with useful details for the program we would like to execute, including the command line. This is where you need to put the shell command to be executed (in the format of a command line tail). Finally, we invoke DOS function 4b00: execute program to execute the command interpreter, running our command.