標籤

2009年4月2日 星期四

MASM note: asume

Since the ds register can be changed at run time (using an instruction like mov ds,ax), any segment can be a data segment.

When you specify a segment in your program, not only must you tell the CPU that a segment is a data segment, but you must also tell the assembler where and when that segment is a data (or code/stack/extra/F/G) segment.

Note that this directive does not modify any of the segment registers, it simply tells the assembler to assume the segment registers are pointing at certain segments.

assume directive modifies the assembler's behavior from the point MASM encounters it until another assume directive changes the stated assumption.


DSEG1 segment para public 'DATA'
var1 word ?
DSEG1 ends

DSEG2 segment para public 'DATA'
var2 word ?
DSEG2 ends

CSEG segment para public 'CODE'
assume CS:CSEG, DS:DSEG1, ES:DSEG2
mov ax, seg DSEG1
mov ds, ax
mov ax, seg DSEG2
mov es, ax

mov var1, 0
mov var2, 0
.
.
.
assume DS:DSEG2
mov ax, seg DSEG2
mov ds, ax
mov var2, 0
.
.
.
CSEG ends
end


The 80x86 microprocessor doesn't know about segments declared within your program, it can only access data in segments pointed at by the cs, ds, es, ss, fs.

When the assembler encounters an instruction of the form mov var1,0, the first thing it does is determine var1's segment. It then compares this segment against the list of assumptions the assembler makes for the segment registers. If you didn't declare var1 in one of these segments, then the assembler generates an error claiming that the program cannot access that variable. If the symbol (var1 in our example) appears in one of the currently assumed segments, then the assembler checks to see if it is the data segment. If so, then the instruction is assembled as described in the appendices. If the symbol appears in a segment other than the one that the assembler assumes ds points at, then the assembler emits a segment override prefix byte, specifying the actual segment that contains the data.

In the example program above, MASM would assemble mov VAR1,0 without a segment prefix byte. MASM would assemble the first occurrence of the mov VAR2,0 instruction with an es: segment prefix byte since the assembler assumes es, rather than ds, is pointing at segment DSEG2. MASM would assemble the second occurrence of this instruction without the es: segment prefix byte since the assembler, at that point in the source file, assumes that ds points at DSEG2. Keep in mind that it is very easy to confuse the assembler.


CSEG segment para public 'CODE'
assume CS:CSEG, DS:DSEG1, ES:DSEG2
mov ax, seg DSEG1
mov ds, ax
.
.
.
jmp SkipFixDS

assume DS:DSEG2

FixDS: mov ax, seg DSEG2
mov ds, ax
SkipFixDS:
.
.
.
CSEG ends
end


Notice that this program jumps around the code that loads the ds register with the segment value for DSEG2. This means that at label SkipFixDS the ds register contains a pointer to DSEG1, not DSEG2. However, the assembler isn't bright enough to realize this problem, so it blindly assumes that ds points at DSEG2 rather than DSEG1. This is a disaster waiting to happen. Because the assembler assumes you're accessing variables in DSEG2 while the ds register actually points at DSEG1, such accesses will reference memory locations in DSEG1 at the same offset as the variables accessed in DSEG2. This will scramble the data in DSEG1 (or cause your program to read incorrect values for the variables assumed to be in segment DSEG2).

For beginning programmers, the best solution to the problem is to avoid using multiple (data) segments within your programs as much as possible. Save the multiple segment accesses for the day when you're prepared to deal with problems like this. As a beginning assembly language programmer, simply use one code segment, one data segment, and one stack segment and leave the segment registers pointing at each of these segments while your program is executing. The assume directive is quite complex and can get you into a considerable amount of trouble if you misuse it. Better not to bother with fancy uses of assume until you are quite comfortable with the whole idea of assembly language programming and segmentation on the 80x86.

The nothing reserved word tells the assembler that you haven't the slightest idea where a segment register is pointing. It also tells the assembler that you're not going to access any data relative to that segment register unless you explicitly provide a segment prefix to an address. A common programming convention is to place assume directives before all procedures in a program. Since segment pointers to declared segments in a program rarely change except at procedure entry and exit, this is the ideal place to put assume directives:


assume ds:P1Dseg, cs:cseg, es:nothing
Procedure1 proc near
push ds ;Preserve DS
push ax ;Preserve AX
mov ax, P1Dseg ;Get pointer to P1Dseg into the
mov ds, ax ; ds register.
.
.
.
pop ax ;Restore ax's value.
pop ds ;Restore ds' value.
ret
Procedure1 endp


The only problem with this code is that MASM still assumes that ds points at P1Dseg when it encounters code after Procedure1. The best solution is to put a second assume directive after the endp directive to tell MASM it doesn't know anything about the value in the ds register:


.
.
.
ret
Procedure1 endp
assume ds:nothing


Although the next statement in the program will probably be yet another assume directive giving the assembler some new assumptions about ds (at the beginning of the procedure that follows the one above), it's still a good idea to adopt this convention. If you fail to put an assume directive before the next procedure in your source file, the assume ds:nothing statement above will keep the assembler from assuming you can access variables in P1Dseg.

Segment override prefixes always override any assumptions made by the assembler. mov ax, cs:var1 always loads the ax register with the word at offset var1 within the current code segment, regardless of where you've defined var1. The main purpose behind the segment override prefixes is handling indirect references. If you have an instruction of the form mov ax,[bx] the assembler assumes that bx points into the data segment. If you really need to access data in a different segment you can use a segment override, thusly, mov ax, es:[bx].

In general, if you are going to use multiple data segments within your program, you should use full segment:offset names for your variables. E.g., mov ax, DSEG1:I and mov bx,DSEG2:J. This does not eliminate the need to load the segment registers or make proper use of the assume directive, but it will make your program easier to read and help MASM locate possible errors in your program.

The assume directive is actually quite useful for other things besides just setting the default segment. You'll see some more uses for this directive a little later in this chapter.

http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_8/CH08-3.html#HEADING3-98

沒有留言: