zoukankan      html  css  js  c++  java
  • 第八章 数据处理的两个基本问题

    为了描述上的简洁,在以后的课程中,我们将使用两个描述性的符号 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

    前三个寄存器我们己经用过了,现在我们进行一下总结。
    (1)在8086CPU 中,只有这4个寄存器(bx、bp、si、di)可以用在“[…]” 中来进行内存单元的寻址。
    (2)在“[…]” 中,这4个寄存器(bx、bp、si、di)可以单个出现,或只能以四种组合出现:
    bx和si、bx和di、bp和si、bp和di

      错误的用法
         mov ax,[bx+bp]
         mov ax,[si+di]
    

    (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 机器指令处理的数据所在位置

    绝大部分机器指令都是进行数据处理的指令,处理大致可分为三类:

    	读取、写入、运算
    

    在机器指令这一层来讲,并不关心数据的值是多少,而关心指令执行前一刻,它将要处理的数据所在的位置。

    8.3 汇编语言中数据位置的表达

    在汇编语言中如何表达数据的位置?

      汇编语言中用三个概念来表达数据的位置。 
      1、立即数(idata)  
      2、寄存器
      3、段地址(SA)和偏移地址(EA)
    

    1、立即数(idata)
    对于直接包含在机器指令中的数据(执行前在cPu 的指令缓冲器中),在汇编语言中称为:立即数(idata ) ,在汇编指令中直接给出。例如:
    mov ax,1
    对应机器码:B80100

    执行结果:(ax) = 1

    2、寄存器
    指令要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名。例如:
    mov ax,bx
    对应机器码:89D8

       执行结果:(ax) = (bx)
    

    3、段地址(SA)和偏移地址(EA)
    指令要处理的数据在内存中,在汇编指令中可用[X]的格式给出EA,SA在某个段寄存器中。
    存放段地址的寄存器可以是默认的。
    存放段地址的寄存器也可以显性的给出。
    存放段地址的寄存器是默认的
    示例:
    mov ax,[0]
    mov ax,[bx]
    mov ax,[bx+8]
    mov ax,[bx+si]
    mov ax,[bx+si+8]

    存放段地址的寄存器是默认的
    示例(续):
    mov ax,[bp]
    mov ax,[bp+8]
    mov ax,[bp+si]
    mov ax,[bp+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
    inc ax
    add ax,1000

    (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无法得知所要访问的单元是字单元,还是字节单元。

    假设我们用Debug查看内存的结果如下:
    2000:1000 FF FF FF FF FF FF……

    那么指令:
    mov ax,2000H
    mov ds,ax
    mov byte ptr [1000H], 1

    将使内存中的内容变为:
    2000:1000 01 FF FF FF FF FF……

    而指令:
    mov ax,2000H
    mov ds,ax
    mov word ptr [1000H],1
    将使内存中的内容变为:
    2000:1000 01 00 FF FF FF FF……

    这是因为
    mov byte ptr [1000H],1访问的是地址为 ds:1000H的字节单元,修改的是 ds:1000H单元的内容;

    而mov word ptr [1000H],1访问的是地址为 ds:1000H 的字单元,修改的是 ds:1000H和ds:1001H两个单元的内容。

    (3)其他方法
    有些指令默认了访问的是字单元还是字节单元

    比如:push [1000H]就不用指明访问的是字单元还是字节单元,

    因为push指令只进行字操作。

    8.6 寻址方式的综合应用

    我们可以看到,8086CPU提供的如[bx+si+idata]的寻址方式为结构化数据的处理提供了方便。

    使得我们可以在编程的时候,从结构化的角度去看待所要处理的数据。

    从上面我们可以看到,一个结构化的数据包含了多个数据项,而数据项的类型又不相同,有的是字型数据,有的是字节型数据,有的是数组(字符串)。

    一般来说,我们可以用[bx+idata+si]的方式来访问结构体中的数据。

    用bx定位整个结构体,用idata定位结构体中的某一个数据项,用 si 定位数组项中的每个元素 。
    为此,汇编语言提供了更为贴切的书写方式。
    如:[bx].idata、[bx].idata[si]。

    在C语言程序中我们看到,如:dec.cp[i],dec是一个变量名,指明了结构体变量的地址,cp 是一个名称,指明了数据项cp的地址,而i用来定位cp中的每一个字符。
    汇编语言中的做法是:bx.10h[si]

    对比一下,是不是很相似?

    8.7 div 指令

    div是除法指令(division),使用div作除法的时候:
    除数:8位或16位,在寄存器或内存单元中
    被除数:(默认)放在AX 或 DX和AX中

    除数 被除数
    8位 16位(AX)
    16位 32位(DX+AX) 分别存放高8位和低8位

    结果:
    运算 8位 16位---------------除数的位数
    商 AL AX
    余数 AH DX

    div指令格式:
    div reg
    div 内存单元

    现在我们可以用多种方法来表示一个内存单元了。

    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)]/((ds)16+0)的商;
    (dx) = [(dx)10000H+(ax)]/((ds)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)的余数

    编程:
    利用除法指令计算100001/100。

    我们首先分析一下,被除数 100001 大于65535,不能用ax寄存器存放,所以我们要用dx和ax两个寄存器联合存放100001,也就是说要进行16位的除法。
    因为要分别为dx和ax赋100001的高16位值和低16位值,所以应先将100001表示为十六进制形式:186A1H。
    编程实现1:
    利用除法指令计算100001/100。(程序)
    mov dx,1
    mov ax,86A1H ;(dx)*10000H+(ax)=100001
    mov bx,100
    div bx
    程序执行后,(ax)=03E8H(即1000),(dx)=1(余数为1)。
    大家可自行在Debug中实践。

    8.8 伪指令 dd

    前面我们用db和dw定义字节型数据和字型数据。

    dd是用来定义dword (double word双字)型数据的。 32位

    示例:data segment
    db 1
    dw 1
    dd 1
    data ends

    在data段中定义了三个数据:
    第一个数据为01H,在data:0处,占1个字节;
    第二个数据为0001H,在data:1处,占1个字;
    第三个数据为00000001H,在data:3处,占2个字节;

    问题8.1
    用div 计算data段中第一个数据除以第二个数据后的结果,商存放在第3个数据的存储单元中。
    data segment
    dd 100001
    dw 100
    dw 0
    data ends
    思考后看分析。
    data segment
    dd 100001
    dw 100
    dw 0
    data ends
    data段中的第一个数据是被除数,为dword(双字)型,32位,所以在做除法之前,用dx和ax存储。应将data:0字单元中的低16位存储在 ax中,data:2字单元中的高16位存储在dx中。

    问题8.1程序实现代码
    mov ax,data
    mov ds,ax
    mov ax,ds:[0] ;ds:0字单元中的低16位存储在ax中
    mov dx,ds:[2] ;ds:2字单元中的高16位存储在dx中
    div word ptr ds:[4] ;用dx:ax中的32位数据除以ds:4字
    ;单元中的数据
    mov ds:[6],ax ;将商存储在ds:6字单元中

    8.9 dup

    dup是一个操作符,在汇编语言中同db、dw、dd 等一样,也是由编译器识别处理的符号。

    它是和db、dw、dd 等数据定义伪指令配合使用的,用来进行数据的重复。
    dup示例
    db 3 dup (0)
    定义了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,则必须用这样的格式:
    stack segment
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    stack ends
    当然,读者可以用dd,使程序变得简短一些,但是如果要求定义一个容量为1000字节或10000字节的呢?

    如果没有dup,定义部分的程序就变得太长了;
    有了dup就可以轻松解决。如下:
    stack segment
    db 200 dup (0)
    stack ends

    实验七-寻址方式在结构化数据访问中的应用

  • 相关阅读:
    【Sqoop】介绍、安装、使用(列出MySQL中数据库/表 + mysql数据导入到Hive表 + Hive表数据导出到mysql表)
    【异常】MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on di
    【异常】Flume启动卡主异常:Agent configuration for 'a2' does not contain any valid channels. Marking it as invalid.
    【异常】转载 kafka.common.MessageSizeTooLargeException
    【异常】转载 ERROR KafkaProducer
    【异常】转载 如何优雅地关闭kafka
    【异常】转载 KAFKA生产者数据丢失问题的排查
    【异常】java.lang.ClassCastException: org.apache.spark.rdd.ShuffledRDD cannot be cast to org.apache.spark.streaming.kafka010.HasOffsetRanges
    mmap
    链表
  • 原文地址:https://www.cnblogs.com/poli/p/4661470.html
Copyright © 2011-2022 走看看