数据处理的两个基本问题

  我们知道,计算机是进行数据处理、运算的机器,那么有两个基本的问题就包含在其中:

(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

 

posted @ 2017-10-22 22:28  佚名000  阅读(258)  评论(0)    收藏  举报