UMBC CMSC 211

UMBC | CSEE


Redirecting stderr

There is no way from the command line to redirect the output sent to stderr. However, it is easy to do inside an assembly language program. One way to solve this problem is to write a program that will DOS and UNIX solve this problem in the same way. Close the stderr file (which has the handle of 2). Since 0 and 1 are opened automatically, when you open another file, it will get the handle 2 that stderr used to have. When one program (call the child) is started by another one (called the parent), the child inherits the open files of the parent.

For the parent to start another program, it is necessary to use the DOS EXEC function. The child is loaded into memory above the parent and is executed until it terminates, when control is returned to the parent, which has been doing nothing, just waiting for the child to finish.

To call the EXEC function, you must set up the registers with the following parameters:

AH 4Bh
AL 0
DS:DX the address of the null-terminated name of the child program. It must have the full pathname and extension. The PATH variable is not used to find the file! The DOS file COMMAND.COM can search the PATH and execute .BAT files.
ES:BX is the address of a 7-word parameter block

Parameter Block
Offset Size Description
00h 2 segment pointer to environment (if 0, use the same block as the parent). Must be paragraph aligned and consist of a sequence of ASCIIZ strings,
02h 4 pointer to the command-tail line. Contains a byte count (which does not include the carriage return at the end). The first character in the string must be a blank.
06h 4 pointer to the first FCB to be copied into the new PSP (author says FCBs are not used and you can use -1
0Ah 4 pointer to the second FCB

Before using this function, you must ensure that there is enough unallocated memory available for the child process. If necessary, you must release all the memory to load the child process. If you don't, you will get an out of memory error.

The word at 02h in the PSP is the segment address of the next memory location available for allocation, and normally that will be A000h, which is all of available memory is allocated to the expectant parent and no memory is available for the child program.

Use the realloc DOS call to modify the amount of memory allocation. We set it up with the following parameters:

AH 4Ah
BX new memory size in paragraphs
ES segment of block address
If this works, the BX register will contain the maximum number of paragraphs available. If it does not work (le. If it does not work (Carry Flag is set), the AX register will contain the error code:
07h damaged memory control block
08h insufficient memory
09h incorrect memory block addres

The William Jones gives the following about how to change the allocation:

In order to release unnecssary memory, we must know how much memory we are actually using. The number of paragraphs is (last segment + number of paragraphs in the last segment - first segment). The first segment if the PSP (whose segment address will be in ES register when the program first starts) and the last segment is always the stack segment. the SS register holds the address of the low memory portion of the stack. There we must add the number of paragraphs that we allocated in the .STACK parameter. We must convert that to number of paragraphs (and since it might not be an exact number of paragraphs, we must round up so that we do not over-write the contents of the stack). We can do that with the formula: SS + (stacksize + 15) / 16 - ES Now we know how much memory to keep and DOS will figure out how much to release.

Remember the parent just sits there, not doing a thing, until the child is finished. When returning from the EXEC call, the Carry Flag will be clear if everything went Oill be clear if everything went OK. However, the EXEC call only saved the CS register. The SS and SP registers will be clobbered, and the only thing the parent knows about when it returns, so the parent must save the SS and SP values in the .CODE segment! Then when the parent starts back up, it must restore the SS and SP values before it does anything else.

Let's look at an example of how to do this.

INCLUDE    PCMAC.INC
           .MODEL     SMALL
           ASSUME     SS:NOTHING

STACKSIZE  EQU        100h
           .STACK     STACKSIZE

PSP        SEGMENT AT 0-256
           ORG        2cH
EnvirSeg   DW         ?
PSP        ENDS

           .DATA
CmdFile    DB         'cmdtail.exe', 
CmdTail    DB         17, ' the command tail ',13
ParmBlock  DW         ?
           DD         CmdTail
           DD         -1, -1;           ; Null FCBs

           .CODE
           EXTRN      CCheck : NEAR


SaveSS     DW         ?                 ; This is different, 
SaveSP     DW         ?                 ;  must be in code segment!


execer     PROC
           mov        ax, @data
           mov        ds, ax
           ASSUME     es:PSP
           mov        ax, EnvirSeg      ; Get the string of environment
                                        ;  variables from the parent
           mov        ParmBlock
;
; Figure out how much memory to keep and then release the
;   memory that is extra.
;        SS + (stacksize + 15) / 16 - ES
:   What we have to do is adjust the address up to the start
;        of the next memory paragraph.
; 
           mov        bx, ss;           ; Paragraph of last segment

           add        bx, (STACKSIZE+15)/16; since this is all known 
                                        ; at assembly time, this is 
                                        ; reduced to a simple constant
                                        ; by the assembler
           mov        ax, es            ; can't do math with the ES register
           sub        bx, ax            ; bx has the program size now
           mov        ah, 4Ah           ; REALLOC function
           int        21
           call       CCheck            ; Abort if memory could not be released

           mov        SaveSP, sp
           mov        SaveSS, ss
           push       ds
           pop        es
           ASSUME     es:@data
	   mov        dx, OFFSET CmdFile   ; ds:dx --> file to EXEC
           mov        bx, OFFSET ParmBlock ; es:bx --> parameter block
           mov        ax, 4B00h
           int        21
           ASSUME     ds:NOTHING, es:NOTHING
;

           cli                          ; Don't let an interrupt occur
                                        ; until the stack is restored or
                                        ; everything is lost!

;
           mov        ss, SaveSS        ; mov instruction does not change
           mov        sp, SaveSP        ;    the flags!


           sti	                        ; Turn the interrupts back on
                                        ;   as soon as possible!

;
; Stack is now restored and the carry flag from the interrupt is in
;    the flag register 
;

           call       CCheck

           _Exit      0

execer     ENDP
           END        execer           


UMBC | CSEE