在8086CPU中,哪些寄存器可以用"[]"来进行内存单元寻址,并且用该寄存器寻址的段地址是哪一个,都是我们必须明确的.了解汇编只是为了更好的对内存的掌控,更好的编程

一、语法(Syntax)

1.在8086CPU中,只有这四个寄存器(BX,BP,SI,DI)可以用"[]"来进行内存单元寻址。比如下面的指令都是正确的:

mov ax,[bx]
mov ax,[bx+si]
mov ax,[bx+di]
mov ax,[bp]
mov ax,[bp+si]
mov ax,[bp+di]

而下面的指令是错误的:

mov ax,[cx]
mov ax,[ax]
mov ax,[dx]
mov ax,[ds]

2.在[]中,这四个寄存器可以当个出现,或只能以四种组合出现:BX和SI、BX和DI、BP和SI、BP和DI,比如下面的指令都是正确的(idata位常量):

mov ax,[bx]
mov ax,[si]
mov ax,[di]
mov ax,[bp]
mov ax,[bx+si]
mov ax,[bx+di]
mov ax,[bp+si]
mov ax,[bp+di]
mov ax,[bx+si+idata]
mov ax,[bx+di+idata]
mov ax,[bp+si+idata]
mov ax,[bp+di+idata]

下面的指令是错误的:

mov ax,[bx+bp]
mov ax,[si+di]

3.只要在[]中使用BP,而指令中没有显示地指定段地址,段地址就默认在SS中,比如:

mov ax,[bp];等价于 mov ax,ss:[bp]

二、寻址方式于编译器的应用

下面举两个例子进行实验:

  • 用于结构体 [].idata(编译器实现情况有所差异)
  • 用于数组 idata[](编译确实这样实现)

关于第一点可能你比较少见(如果你仅仅是学习汇编),下面举一个例子,接下来将进一步叙述:

assume cs:code
data segment
    db '123456789'
data ends
code segment
start:
    mov ax,data
    mov ds,ax
    mov bx,0
    mov dl,[bx].3
    mov ah,2
    int 21h
    mov ax,4c00h
    int 21h
code ends 
end start

输出结果为:4

首先,考虑一下一维数组的寻址:

#include "iostream"
using namespace std;
int main()
{
    printf("%c\t",3["abcd"]);
    system("pause");
    return 0; 
}

首先我们观察到第一个输出函数,这行语句看起来十分诡异,但是我们知道数组a[3]等价于(a+3),那么同理3[a]是等价于(3+a),这样的话这行代码没什么问题,现在我们来看看这行代码的反编译之后的汇编指令:

010E1916  mov         eax,1  
010E191B  imul        ecx,eax,3  
010E191E  movsx       edx,byte ptr string "abcd" (010E7BD8h)[ecx]  
010E1925  push        edx  
010E1926  push        offset string "%c\t" (010E7B34h)  
010E192B  call        _printf (010E104Bh)  
010E1930  add         esp,8  

接下来我们反汇编printf("%c\t","abcd"[3]);

01111916  mov         eax,1  
0111191B  imul        ecx,eax,3  
0111191E  movsx       edx,byte ptr string "abcd" (01117BD8h)[ecx]  
01111925  push        edx  
01111926  push        offset string "%c\t" (01117B34h)  
0111192B  call        _printf (0111104Bh)  
01111930  add         esp,8  

我们可以清楚的发现,第三行的寻址方式并没有发生任何改变,都是idata[]的方式

二维数组的情况将不予赘述,下面考虑结构体的情况:

#include "iostream"
using namespace std;
struct T {
    int a;
    int b;
};
int main()
{
    T *q = new T ; 
    q->b = 7;
    system("pause");
    return 0; 
}

对q->b = 7;进行反编译得到汇编代码

01271A59  mov         eax,dword ptr [q]  
01271A5C  mov         dword ptr [eax+4],7  

我们发现寻址方式是dword ptr [eax+4],7,等价于 [eax].4

最后一个有意思的程序,可能是某公司的面试题(面试的时候可能不会给出汇编代码)

#include "iostream"
using namespace std;
struct T {
    int a;
    int b;
};
int main()
{
    T t = { 1,2 };
    void *p = &t;
    printf("%d",*(int *)&(((char *)p)[(size_t)&(((T*)0)->b)]));
    system("pause");
    return 0; 
}
010E1A46  mov         eax,1  
010E1A4B  shl         eax,2  
010E1A4E  mov         ecx,dword ptr [p]  
010E1A51  mov         edx,dword ptr [ecx+eax]  
010E1A54  push        edx  
010E1A55  push        offset string "%d" (010E7C60h)  
010E1A5A  call        _printf (010E1050h)  
010E1A5F  add         esp,8  

输出结果:2(注意到第四行汇编[ecx+eax]为成员b的地址)