前面已经讲述了几种不同的寻址方式,这些寻址方式总结出来,就在于偏移地址的表示方式,偏移地址可以用常量也可以用变量,或者常量加上变量。这些方式都是可以接收的。另外8086CPU还设置了两个独立的寄存器SI,DI用来辅助BX,这两个寄存器都是16位的,因此,不能分成两个8位,同时书写方式也同前面所述的一样,可以灵活如下:
D:\Temp\NPP593~1.BIN\tmp>debug
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1400 ES=1400 SS=1400 CS=1400 IP=0100 NV UP EI PL NZ NA PO NC
1400:0100 0000 ADD [BX+SI],AL DS:0000=CD
-d 2000:1000 BE 00 06 00 00 00
^ Error
-e 2000:1000 BE 00 06 00 00 00
-d 2000:1000
2000:1000 BE 00 06 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
2000:1010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
2000:1020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
2000:1030 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
2000:1040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
2000:1050 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
2000:1060 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
2000:1070 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-a
1400:0100 mov ax,2000
1400:0103 mov ds,ax
1400:0105 mov bx,1000
1400:0108 mov si,0
1400:010B mov ax,[bx+si]
1400:010D inc si
1400:010E mov cx,[bx+si]
1400:0110 inc si
1400:0111 mov di,si
1400:0113 add cx,[bx+di]
1400:0115
-t
AX=2000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1400 ES=1400 SS=1400 CS=1400 IP=0103 NV UP EI PL NZ NA PO NC
1400:0103 8ED8 MOV DS,AX
-t
AX=2000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=0105 NV UP EI PL NZ NA PO NC
1400:0105 BB0010 MOV BX,1000
-t
AX=2000 BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=0108 NV UP EI PL NZ NA PO NC
1400:0108 BE0000 MOV SI,0000
-t
AX=2000 BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=010B NV UP EI PL NZ NA PO NC
1400:010B 8B00 MOV AX,[BX+SI] DS:1000=00BE
-t
AX=00BE BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=010D NV UP EI PL NZ NA PO NC
1400:010D 46 INC SI
-t
AX=00BE BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0001 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=010E NV UP EI PL NZ NA PO NC
1400:010E 8B08 MOV CX,[BX+SI] DS:1001=0600
-t
AX=00BE BX=1000 CX=0600 DX=0000 SP=FFEE BP=0000 SI=0001 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=0110 NV UP EI PL NZ NA PO NC
1400:0110 46 INC SI
-t
AX=00BE BX=1000 CX=0600 DX=0000 SP=FFEE BP=0000 SI=0002 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=0111 NV UP EI PL NZ NA PO NC
1400:0111 89F7 MOV DI,SI
-t
AX=00BE BX=1000 CX=0600 DX=0000 SP=FFEE BP=0000 SI=0002 DI=0002
DS=2000 ES=1400 SS=1400 CS=1400 IP=0113 NV UP EI PL NZ NA PO NC
1400:0113 0309 ADD CX,[BX+DI] DS:1002=0006
-t
AX=00BE BX=1000 CX=0606 DX=0000 SP=FFEE BP=0000 SI=0002 DI=0002
DS=2000 ES=1400 SS=1400 CS=1400 IP=0115 NV UP EI PL NZ NA PE NC
1400:0115 0000 ADD [BX+SI],AL DS:1002=06
-
总结一下前面所述的寻址方式如下:
【1】[idata]用一个常量来表示地址,可以直接定义一个内存单元,在MASM中用ds:[20],段前缀
【2】[bx]用一个变量来表示内存地址,可以间接定义一个内存单元,这里除了bx之外还可以si,di
【3】[bx+idata]用一个变量加上常量来表示内存地址,可在一个起始地址的基础上用变量间接定位一个内存单元,常用的写法有idata[bx]或[idata+bx]及[bx].idata
【4】[bx+si]用两个变量来表示地址
【5】[bx+si+idata]用两个变量加一个常量来表示地址,其写法同第三个一样也有三种。
D:\Temp\NPP593~1.BIN\tmp>debug
-e 2000:1000 BE 00 06 00 00
-a
1400:0100 mov ax,2000h
^ Error
1400:0100 mov ax,2000
1400:0103 mov ds,ax
1400:0105 mov bx,1000
1400:0108 mov ax,[bx]
1400:010A mov cx,[bx+1]
1400:010D add cx,[bx+2]
1400:0110
-t
AX=2000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1400 ES=1400 SS=1400 CS=1400 IP=0103 NV UP EI PL NZ NA PO NC
1400:0103 8ED8 MOV DS,AX
-t
AX=2000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=0105 NV UP EI PL NZ NA PO NC
1400:0105 BB0010 MOV BX,1000
-t
AX=2000 BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=0108 NV UP EI PL NZ NA PO NC
1400:0108 8B07 MOV AX,[BX] DS:1000=00BE
-t
AX=00BE BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=010A NV UP EI PL NZ NA PO NC
1400:010A 8B4F01 MOV CX,[BX+01] DS:1001=0600
-t
AX=00BE BX=1000 CX=0600 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2000 ES=1400 SS=1400 CS=1400 IP=010D NV UP EI PL NZ NA PO NC
1400:010D 034F02 ADD CX,[BX+02] DS:1002=0006
-t
从汇编指令的角度来说,指令关心的是数据存储在什么位置,数据的长度是多长,至于说内容是不关注的,这些是业务去关注的。从描述的方便的角度,我们使用reg表示寄存器,通常包括16位的ax,bx,cx,dx,sp,bp,si,di以及8位的ah,al,bh,bl,ch,cl,dh,dl。用sreg表示段寄存器,通常包括ds,ss,cs,es。在8086CPU中用来表示内存偏移地址的只有bx,si,di和bp,这四个,注意,这当中还有一个两两互斥的关系,也就是bx与bp不能同时使用,si和di也是,但是bx+si,bx+di,bp+si,bp+di是可以的。默认的 bx的段寄存器是ds,默认的bp的段寄存器是ss。这一点要注意,它跟栈共用。机器指令通常大部分进行的是数据处理,所关注的数据通常在CPU内部寄存器、内存、端口等。
正如前面所讨论的,表示一个数据在内存或都寄存器中的方法,有如下三种:
【1】立即数(idata),直接包含在机器指令中的数据(执行前在CPU的指令缓冲器中),mov ax,1
【2】寄存器,指令要处理的数据在寄存器中,mov ds,ax
【3】段地址和偏移地址, mov ax,[0] mov ax,[di]及mov ax,ds:[bp]
前面讲述的是数据的位置,也就是数据存放的地址,那么数据的长度怎么表示呢?通常寄存器默认是有规定的长度,如ax 16位,al 8 位。等如果没有指明寄存器情况下,使用word和byte +ptr来指明。mov word ptr ds:[0],1 或mov byte ptr ds:[0],1。其它像push默认就是字方式。到这里我们讲了数据段,数据在内存中的存放定义。我们使用dw,db来定义内存数据,但是我们想一想,如果有很多数据怎么办?而这些数据都是重复的,那么是不是要写死了。汇编中设计了一个由编译器识别的符号dup。dup可用定义重复的数据 db 3 dup (0) 定义了3个字节,相当于db 0,0,0 同理db 3 dup (0,1,2) 相当于定义了9个字节 db 0,1,2,0,1,2,0,1,2。提到数据的定义,这里还有一个符号也是需要提到的就是dd. dd是用来定义dworld(double word,双字型数据的),结合前面对数据的叙述,我们可以总结如下:
【1】db,dw,dd均可以用在数据段来定义内存中的数据
【2】dup用来定义数据段的重复数据。
前面我们讲了加法add指令,那么除法怎么做呢?汇编中有现成的除法指令div(division)。不过考虑到CPU只能做加法,对除法是通过加法进行模拟的。因此,除法需要注意位数。通常除数的大小有8位与16位两种,这只针对8086CPU架构来说。8080CPU对除法是进行加法模拟的,因此被除数必须有两块地方来存储商与余数。如果除数有8位,那么被除数肯定不小于8位,除得结果也可能是8位,因此余数就没地方放了,所以如果除数是8位,那么被除数一定是16位,同样,如果除数是16位,被除数是32位。
dive byte ptr ds:[0]
(al) = (ax)/((ds)*16+0) 的商
(ah)=(ax)/((ds)*16+0)的余数
div word ptr es:[0]
(ax) =[(ds)*10000H+(ax)]/((es)*16+0)的商
(dx)=[(ds)*10000H+(ax)]/((es)*16+0)的余数
从上面两道题的结果来看,进行除法运算时,需要先根据被除数大小,也就占用字节数,来按排除数占用的字节数,这个适用于除数可以用存储为8个字节也可以16个字节,如果除数只能16个字节,那就无法选择,只能32个字节的被除数。