为什么 SEG 不给出带有此代码片段的错误消息?

Why does SEG not give an error message with this code fragment?

下面一行没有汇编程序产生的错误:

mov ax,seg TEXT:frewd

(见下面的程序片段)

我希望汇编程序创建一条错误消息,因为 frewd 不在 TEXT 段中但在 TEXT1 段中并且不存在 GROUP 语句.

我是不是漏掉了什么?

我已经用虚拟数据填充了两个段,所以有 2 个不同的段,但仍然没有错误。

 .386
 TEXT segment para private
   example dw ?
   dummy byte 65531 dup(0)
   sample dw ?
 TEXT ends
 TEXT1 segment word private 'CODE'
   frew dw ?
   dummy1 byte 65531 dup(0)
   frewd dw ?
 TEXT1 ends
 Cseg segment
   mov ax, seg TEXT:frewd ;no error is generated here by the assembler
   mov es,ax
 Cseg ends
 end

MASM 没有理由给出这一行的错误。 SEG 运算符获取它所使用的表达式的帧的段地址(实模式下的段落地址)。帧是地址的最终偏移量由 linker 确定的段或组。默认情况下,地址的帧与地址段相同,除非地址段属于一个组。请注意,这意味着 MASM 中的地址包含三个部分,一个帧、一个段和一个偏移量。

由于 TEXT:frewd 是一个地址,这意味着它有一个框架,因此 SEG 运算符计算该表达式的框架。事实证明,表达式 TEXT:frewd 的框架并不是它看起来的样子,但如果它实际上是 TEXT,那么 MASM 就没有理由不能无误地计算表达式. frewd 不在 TEXT 基数 64k 以内的问题直到 link 时间才知道。

MASM只允许一个地址的帧与一个地址的段不同,如果该帧是组的话。这意味着当段名称用作标签的段覆盖时,它实际上不会将地址的帧更改为命名段。相反,如果框架曾经是一个组,则框架将更改为标签段的框架,否则框架将与标签段保持不变。但是,如果在内存操作数中使用标签,则段覆盖确实会根据先前的 ASSUME 语句更改 MASM 将用于指令的段寄存器。

我创建了一个示例来尝试演示 MASM 的行为方式。从内存加载值的每条 MOV 指令都注释了汇编器用于该指令的段寄存器 ("sreg") 以及用于内存操作数地址的帧和段的说明。使用的帧和段出现在汇编器放入它输出的目标文件的重定位(或修正)中。

DATA1   SEGMENT PARA
data1_label DW  0
DATA1   ENDS

DATA2   SEGMENT PARA
    DW  2
data2_label DW  4
DATA2   ENDS

DGROUP  GROUP   DATA1, DATA2

FARDATA SEGMENT PARA
    DW  6, 8
fardata_label DW 10
FARDATA ENDS

CODESEG SEGMENT PARA PUBLIC 'CODE'
start:
    mov ax, DGROUP
    mov ds, ax
    mov ax, FARDATA
    mov es, ax
    mov ax, DATA2
    mov ss, ax
    ASSUME  ds:DGROUP
    ASSUME  es:FARDATA
    ASSUME  ss:DATA2
    ASSUME  cs:CODESEG

    ; Since data1_label and data2_label belong to segments that belong to DGROUP,
    ; they're accessed relative to DGROUP by default and through the segment 
    ; register assumed to point to DGROUP. The correct code is generated
    ; without any overrides.

    mov ax, [data1_label]           ; sreg: DS, frame: DGROUP,  segment: DATA1
    mov ax, [data2_label]           ; sreg: DS, frame: DGROUP,  segment: DATA2

    ; No surprises here, fardata_label is accessed relative to the segment its
    ; defined in and using the segment register assumed for that segment.

    mov ax, [fardata_label]         ; sreg: ES, frame: FARDATA, segment: FARDATA

    ; Changing the last three instructions to use DATA2 as a segment override causes
    ; them all to use the SS segment register, the one assumed for SS.  It also
    ; overrides using DGROUP as the frame for data1_label and data2_label, but
    ; doesn't change the frame to DATA2 for either data1_label or fardata_label.
    ; Only the second instruction will work correctly.  The other two instructions
    ; use the wrong segment register access the label at the offset the linker will
    ; end up using.

    mov ax, [DATA2:data1_label]     ; sreg: SS, frame: DATA1,   segment: DATA1
    mov ax, [DATA2:data2_label]     ; sreg: SS, frame: DATA2,   segment: DATA2
    mov ax, [DATA2:fardata_label]   ; sreg: SS, frame: FARDATA, segment: FARDATA

    ; Overriding with CODESEG has the same as effect as overriding with DATA2,
    ; except the CS register is used instead. None of the instructions will
    ; work, since none of them will have offsets relative to CODESEG, the segment
    ; loaded into CS.

    mov ax, [CODESEG:data1_label]   ; sreg: CS, frame: DATA1,   segment: DATA1
    mov ax, [CODESEG:data2_label]   ; sreg: CS, frame: DATA2,   segment: DATA2
    mov ax, [CODESEG:fardata_label] ; sreg: CS, frame: FARDATA, segment: FARDATA

    ; Using DGROUP as an override on fardata_label will work so long as
    ; fardata_label doesn't end up getting placed before the start of the DGROUP,
    ; or someplace 64K beyond the start of DGROUP.  If it does end up outside
    ; DGROUP then the linker will give an error.  The assembler is unable to
    ; detect this.

    mov ax, [DGROUP:fardata_label]  ; sreg: DS, frame: DGROUP,  segment: FARDATA

    ; Using a single segment override on a number works as expected, but 
    ; using multiple overrides only the left-most override has an effect. 

    mov ax, [CODESEG:0]             ; sreg: CS, frame: CODESEG, segment: CODESEG
    mov ax, [DGROUP:0]              ; sreg: DS, frame: DGROUP,  segment: DGROUP
    mov ax, [DGROUP:CODESEG:0]      ; sreg: DS, frame: DGROUP,  segment: DGROUP
    mov ax, [CODESEG:DGROUP:0]      ; sreg: CS, frame: CODESEG, segment: CODESEG
    mov ax, [FARDATA:CODESEG:0]     ; sreg: ES, frame: FARDATA, segment: FARDATA
CODESEG ENDS

    END start

如果您更改 MOV 指令以便它们在地址上使用 SEG(例如 mov ax, SEG data1_labelmov ax, SEG DATA2:data1_label),则 SEG 运算符将计算为 "frame" 中给出的内容注释。

这个故事的寓意是,您几乎永远不想使用 MASM 来分割名称作为段覆盖,因为它几乎肯定不会做您想要的。也很少需要使用组名称作为段覆盖,因为汇编程序默认情况下将使用组来定义组中的任何内容。 (请注意,这与 MASM 5 或更早版本不同,如果标签是 DGROUP 的一部分,您必须使用 OFFSET DGROUP:label 才能获得正确的结果。)

在 MASM 中使用段覆盖的唯一真正有用的方法是在左侧使用段寄存器。在这种情况下,它可以替代使用 ASSUME 或者当内存操作数中没有涉及标签时,例如索引寻址(例如 mov ax, es:[di])。