数据处理的两个基本问题
我们知道,计算机是进行数据处理、运算的机器,那么有两个基本的问题就包含在其中:
(1) 要处理的数据在什么地方?
(2) 要处理的数据有多长?
这两个问题,在机器指令中必须给出明确或隐含的说明,否则计算机就无法工作。
我们定义的描述性符号:reg 和 sreg。
reg 表示一个寄存器,sreg 表示一个段寄存器。
reg 的集合包括:ax、bx、cx、dx、ah、al、bh、bl、ch、cl、dh、dl、sp、bp、si、di;
sreg 的集合包括:ds、ss、cs、es。
8.1 bx、si、di 和 bp
前 3 个寄存器我们已经用过了,现在我们进行一下总结。
(1) 在 8086CPU 中,只有这 4 个寄存器可以用在 “[...]”中来进行内存单元的寻址。比如下面的指令都是正确的:
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) 在 [...] 中,这 4 个寄存器可以单个出现,或只能以 4 种组合出现:bx 和 si、bx 和 di、bp 和 si、bp 和 di。如:
mov ax,[bx]
mov ax,[bp]
mov ax,[si]
mov ax,[di]
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]
(3)只要在 [...] 中使用寄存器 bp,而指令中没有显式地给出段地址,段地址就默认在 ss 中。比如下面的指令。
mov ax,[bp] 含义:(ax) = ((ss)*16 + (bp))
mov ax,[bp+idata] 含义:(ax) = ((ss)*16 + (bp) + idata)
mov ax,[bp+si] 含义:(ax) = ((ss)*16 + (bp) + (si))
mov ax,[bp+si+idata] 含义:(ax) = ((ss)*16 + (bp) + (si) + idata)
8.2 机器指令处理的数据在什么地方
CPU 内部(寄存器、指令缓冲器)、内存(mov ax,[0])、端口。
8.3 汇编语言中数据位置的表达
(1)立即数(idata)
对于直接包含在机器指令中的数据(执行前在 CPU 的指令缓冲器中),在汇编语言中称为:立即数(idata),在汇编语言中直接给出。
例如:mov ax,1
add bx,2000h
or bx,00010000b
mov al,'a'
(2)寄存器
指令要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名。
例:mov ax,bx
mov ds,ax
push bx
mov ds:[0],bx
push ds
mov ss,ax
mov sp,ax
(3)段地址(SA) 和 偏移地址(EA)
指令要处理的数据在内存中,在汇编指令中可用[X]的格式给出 EA,SA 在某个段寄存器中。
存放段地址的寄存器可以是默认的,比如:
mov ax,[0]
mov ax,[di]
mov ax,[bx+8]
mov ax,[bx+si]
mov ax,[bx+si+8]
等指令,段地址默认在 ds 中。
mov ax,[bp]
mov ax,[bp+8]
mov ax,[bp+si]
mov ax,[bx+si+8]
等指令,段地址默认在 ss 中。
存放段地址的寄存器也可以是显性给出的,比如以下的指令。
mov ax,ds:[bp] 含义:(ax) = ((ds)*16 + (bp))
mov ax,es:[bx] 含义:(ax) = ((es)*16 + (bx))
mov ax,ss:[bx+si] 含义:(ax) = ((ss)*16 + (bx) + (si))
mov ax,cs:[bx+si+8] 含义:(ax) = ((cs)*16 + (bx) + (si) + 8)
8.4 寻址方式
当数据存放在内存中的时候,我们可以用多种方式来给定这个内存单元的偏移地址,这种定位内存单元的方法一般被称为寻址方式。

8.5 指令要处理的数据有多长
8086CPU 的指令,可以处理两种尺寸的数据,byte 和 word。所以在机器指令中要指明,指令进行的是字操作还是字节操作。
(1) 通过寄存器名指明要处理的数据的尺寸。
例如,下面的指令中,寄存器指明了指令进行的是字操作。
mov ax,1
mov bx,ds:[0]
mov ds,ax
mov ds:[0],ax
mov ax
mov ax,100
下面的指令中,寄存器指明了指令进行的是字节操作。
mov al,1
mov al,bl
mov al,ds:[0]
mov ds:[0],al
inc al
add al,100
(2) 在没有寄存器名存在的情况下,用操作符 X ptr 指明内存单元的长度,X 在汇编中可以为 word 或 byte。
例如,下面的指令中,word ptr 指明了指令访问的内存单元是一个字单元。
mov word ptr ds:[0],1
inc word ptr [bx]
inc word ptr ds:[0]
add word ptr [bx],2
下面的指令中,用 byte ptr 指明了指令访问的内存单元是一个字节单元。
mov byte ptr ds:[0],1
inc byte ptr [bx]
inc byte ptr ds:[0]
add byte ptr [bx],2
在没有寄存器参与的内存单元访问指令中,用 word ptr 或 byte ptr 显性地指明所要访问的内存单元的长度是很有必要的。否则,CPU 无法得知所要访问的单元是字单元,还是字节单元。
(3) 其他方法
有些指令默认了访问的是字单元还是字节单元,比如,push [1000h] 就不用指明访问的是字单元还是字节单元,因为 push 指令只进行字操作。
8.7 div 指令
div 是除法指令,使用 div 指令做除法的时候应注意以下问题。
(1) 除数:有 8 位和 16 位两种,在一个 reg 或内存单元中。
(2)被除数:默认放在 AX 或 AX 和 DX 中,如果除数为 8 位,被除数则为 16 位,默认在 AX 中存放;如果除数为 16 位,被除数则为 32 位,在 DX 和 AX 中存放,DX 存放高 16 位,AX 存放低 16 位。
(3) 结果:如果除数为 8 位,则 AL 存储除法操作的商,AH 存储除法操作的余数;如果除数为 16 位,则 AX 存储除法操作的商,DX 存储除法操作的余数。
格式如下:
div reg
div 内存单元
现在,我们可以用多种方法来表示一个内存单元了,比如下面的例子:
div byte ptr ds:[0]
含义:(al) = (ax) / ((ds)*16 + 0) 的商
(ah) = (ax) / ((ds)*16 + 0) 的余数
div word ptr es:[0]
含义:(ax) = [(dx)*10000H + (ax)] / ((es)*16 + 0) 的商
(dx) = [(dx)*10000H + (ax)] / ((es)*16 + 0) 的余数
div byte ptr [bx+si+8]
含义:(al) = (ax) / ((ds)*16 + (bx) + (si) + 8) 的商
(ah) = (ax) / ((ds)*16 + (bx) + (si) + 8) 的余数
div word ptr [bx+si+8]
含义:(ax) = [(dx)*10000H + (ax)] / ((ds)*16 + (bx) + (si) + 8) 的商
(dx) = [(dx)*10000H + (ax)] / ((ds)*16 + (bx) + (si) + 8) 的余数
8.8 伪指令 dd
前面我们用 db 和 dw 定义字节型和字型数据。dd 是用来定义 dwords (double word,双字)型数据的。比如:
data segment
db 1
dw 1
dd 1
data ends
在 data 段中定义了 3 个数据:
第一个数据为 01H,在 data:0 处,站 1 个字节。
第二个数据为 0001H,在 data:1 处,占 1 个字(两个字节)。
第三个数据为 00000001H,在 data:3 处(一个字两个字节,一个内存单元一个字节),占 2 个字。
问题:用 div 计算 data 段中第一个数据除以第二个数据后的结果,商存在第三个数据的存储单元中。
data segment
dd 100001
dw 100
dw 0
data ends
分析:100001 为 dword 型,32 位,所以在做除法前,要使用 AX 存储低 16 位,DX 存储高 16 位。程序如下:
mov ax,data
mov ds,ax
mov ax,[0] ;ds:0 字单元中的低 16 位存储在 ax 中
mov dx,[2] ;ds:2 字单元中的高 16 位存储在 dx 中
div word ptr ds:[4]
mov ds:[6],ax
这里有个问题是,为什么[0]开始的 16 位是低 16位?这就涉及到之前讲过的,内存单元编址的问题,内存编址是从 0 开始,由小到大一个字节一个字节编的。也就是说,使用调试工具查看到的是 af01,这实际上是 01af(这里需要转为二进制,01 和 af分别转二进制然后连在一起,实际上是 110101111,这里的最低位是 f(1111))。
8.9 dup
dup 是一个操作符,在汇编语言中同 db、dw、dd 等一样,也是由编译器识别处理的符号。它是和 db、dw、dd 等数据定义伪指令配合使用的,用来进行数据的重复。比如:
db 3 dup (3)
定义了 3 个字节,它们的值都是 0,相当于 db 0,0,0。
db 3 dup (0,1,2) 定义了 9 个字节,它们是 0、1、2、0、1、2、0、1、2,相当于 db 0,1,2,0,1,2,0,1,2。
db 3 dup ('abc', 'ABC')
定义了 18 个字节,它们是 'abcABCabcABCabcABC',相当于 db 'abcABCabcABCabcABC'。
可见,dup 的使用格式如下:
db 重复的次数 dup (重复的字节型数据)
dw 重复的次数 dup (重复的字型数据)
dd 重复的次数 dup (重复的双字节型数据)
dup 是一个十分有用的操作符,比如要定义一个容量为 200 个字节的栈段,如果不使用 dup,则必须使用每个字节都定义出来,如果使用 dup,则简洁很多:
stack segment
db 200 dup (0)
stack ends

浙公网安备 33010602011771号