zoukankan      html  css  js  c++  java
  • 《汇编语言》(王爽)笔记

    1、有多少根控制总线,就意味着CPU提供了对外部器件的多少种控制。

    2、汇编指令 伪指令

    3、CPU通过总线控制接口,接口控制设备

    4、CPU对物理器件的操作,通过控制线发出内存读写命令,把他们都当作内存来对待。所有的物理存储器被看作一个由若干存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中张有一个地址段,即一段地址空间。CPU在这段地址空间中读写数据,实际上就是在相对应的物理存储器中读写数据。

    5、内部总线实现CPU内部各个器件之间的联系,外部总线实现CPU和主板上其他器件的联系。

    6、

    mov ax,8226
    mov bx,ax
    add ax,bx

    ax + bx = 1044CH
    ax = 044CH(最高位并不丢弃,只是存不下)

    7、

    mov ah,0
    mov al,C5H
    add al,93H

    al + 93H = 158H
    ax = 0058H(此时al作为一个独立的8位寄存器来使用的,和ah没有关系,CPU在执行这条指令是认为ah和al是两个不相关的寄存器。不要错误的认为,诸如add al,93H的指令产生的进位会存储在ah中,add al,93H 进行的是8为运算)

    add ax,93H
    ax =
    0158H(如果执行add ax,93H ,低8位的进位会存储在ah中,CPU在执行这条指令时认为只有一个16位寄存器ax,进行的是16位运算。)

    8、 在8086PC机中,存储单元的地址用两个元素来描述,即段地址和偏移地址
    段地址*16 + 偏移地址 = 物理地址
    一个段的起始地址一定是16的倍数,一个段的长度最大为64KB

    9、 4个段寄存器:cs、ds、ss、es

    10、CS、IP是8086CPU中两个最关键的寄存器,它们指示了CPU当前要读取指令的地址。CS为代码段寄存器,IP为指令指针寄存器。在8086PC机中,任意时刻,设CS中的内容为M,IP中的内容为N,8086CPU将从内存M*16+N单元开始,读取一条指令并执行。(CS:IP)

    11、8086CPU工作过程
    (1)从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器
    (2)IP = IP + 送读取指令的长度,从而指向下一条指令
    (3)执行指令。转到步骤(1),重复这个过程

    12、ax、bx、cx、dx 可以用mov指令来改变,mov指令被称为传送指令,但是mov指令不能用于设置cs、ip的值,原因很简单,因为8086CPU没有提供这样的功能。8086CPU为CS、IP提供了另外的指令来改变它们的值。能够改变CS、IP的内容的指令被统称为转移指令。
    jmp 2AE3:3,执行后:CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令
    若想仅修改IP的内容,可用形如“jmp 某一合法寄存器”的指令完成,如
    jmp ax,指令执行前:ax=1000H,cs=2000H,ip=0003H
    指令执行后:ax=1000H,cs=2000H,ip=1000H

    13、Debug指令

    R P   A T   E D U

    14、[]说明操作对象是一个内存单元,[]中是内存单元的偏移地址,它的段地址默认放在ds中。
    如何把一个数据送入ds呢?我们以前用类似“mov ax,1”这样的指令来完成,从理论上讲,我们可以用相似的方式:mov ds,1000H,来将1000H送入ds。可是,现实并非如此,8086CPU不支持将数据直接送入段寄存器的操作,ds是一个段寄存器,所以 mov ds,1000H这条指令是非法的。那么如何将1000H送入ds呢?只好用一个寄存器来进行中转,即先将1000H送入一个一般寄存器,如bx,再将bx中的内容送入ds。(立即数到段寄存器,硬件设计问题,他们之间没有通路)

    15、栈顶的段地址存放在SS中,偏移地址存放在SP中。任意时刻,SS:SP指向栈顶元素。push指令和pop指令执行时,CPU从SS和SP中得到栈顶的地址。

    16、如果将10000H~1000FH这段空间当作栈,初始状态栈是空的,此时,SS=1000H,SP=?
    SP=0010H

    17、

    push sp-=2
    pop sp+=2

    18、栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。

    19、Debug的T命令在执行修改寄存器SS的指令时,下一条指令也紧接着被执行。

    20、段结束、程序结束、程序返回

    21、编译时发现的错误:语法错误
    运行时发现的错误:逻辑错误

    22、操作系统是由多个功能模块组成的庞大、复杂的软件系统。任何通用的操作系统,都要提供一个称为shell的程序,用户使用这个程序来操作计算机系统进行工作。DOS中有一个程序command.com,这个程序在DOS中称为命令解释器,也就是DOS系统的shell

    23、CPU执行loop指令需要两步:先减一,后判断,然后跳转
    ①(cx) = (cx)-1 ②判断cx中的值,不为零则转至标号处继续循环
    assume cs:code


    code segment
    mov ax,2

      mov cx,11 ;循环框架,3步
    s:   add ax,ax
      loop s

    mov ax,4c00h
    int 21h ;int指令要用P执行,其他的用t
    code ends


    end

    24、在汇编程序中,数据不能以字母开头,要在前面加0。比如,9138h在汇编源程序中可以直接写为9138h,而a000h在汇编源程序中要写为0a000h。

    25、Debug中,执行到指定地址用g,
    eg: g 0012 ;执行到cs:0012
    Debug中,跳出循环用p指令
    遇到loop 0012时,用p执行

    26、

    (1)在汇编源程序中,如果用指令访问一个内存单元,则在指令中必须用[]来表示内存单元,如果在[]里用一个常量idata直接给出内存单元的偏移地址,就要在[]的前面显示地给出段地址所在的段寄存器。比如:
    mov al,ds:[0]
    如果没有在[]的前面显示地给出段地址所在的段寄存器,比如:
    mov al,[0]
    那么,编译器masm将把指令中的[idata]解释为idata
    (2)如果在[]里用寄存器,比如bx,间接给出内存单元的偏移地址,则段地址默认在ds中。当然,也可以显式地给出段地址所在的段寄存器。

    27、dw define word

    28、and指令,可将操作对象的相应位设为0,其他位不变。or指令,可将操作对象的相应位设为1,其他位不变。
    大小写转换,加减20h,做or或者and运算实现

    29、
    20H
    0010 0000 B
    将第五位置0,相当于减20h
    and al,11011111
    将第五位置1,相当于加20h
    or al,00100000

    30、mov ax,[200+bx]
    mov ax,200[bx] ;类似于高级语言的数组,200相当于数组名,bx相当于下标
    mov ax,[bx].200

    31、bx,bp,si,di
    (1)在8086CPU中,只有这4个寄存器可以用在[]中进行内存单元的寻址。比如下面的指令都是正确的:
    mov ax,[bx]
    mov ax,[bp]
    mov ax,[bx+si]
    mov ax,[bx+di]
    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
    (3)只要在[]中使用寄存器bp,而指令中没有显性地给出段地址,段地址就默认在ss中。比如下面的指令
    mov ax,[bp]
    mov ax,[bp+idata]
    mov ax,[bp+si}
    mov ax,[bp+si+idata]
    其他情况,默认段寄存器为ds


    32、寻址方式
    1、立即数寻址
    2、直接寻址
    [idata]
    3、寄存器间接寻址
    [bx][bp][si][di]
    4、寄存器相对寻址
    [bx+idata][bp+idata][si+idata][di+idata]
    5、基址变址寻址
    [bx+si][bx+di][bp+si][bp+di]
    6、相对基址变址寻址
    [bx+si+idata][bx+di+idata][bp+si+idata][bp+di+idata]


    33、div(da高低余商)
    被除数:如果除数为8位,被除数则为16位,默认放在ax中;如果除数为16位,被除数为32位,dx存放高16位,ax存放低16位。
    结果:如果除数为8位,则al存储除法操作的商,ah存储除法操作的余数;如果除数为16位,则ax存储除法操作的商,dx存储除法操作的余数。


    34、dd define double word


    35、db 3 dup(0) 定义了3个字节

    db 3 dup(0,1,2) 定义了9个字节
    db 3 dup('abc','ABC') 定义了18个字节


    36、jmp short 标号//段内短转移,它对IP的修改范围为-128~127
    jmp near ptr 标号//段内近转移,它对IP的修改范围为-32768~32767
    jmp far ptr 标号//段间转移,cs=标号所在段的段地址;ip=标号所在段的偏移地址


    (机器码中,转移位移的计算方法)


    37、转移地址在内存中的jmp指令
    jmp word ptr 内存单元地址(段内转移)
    jmp dword ptr 内存单元地址(断间转移,从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址)


    38、所有的有条件转移指令都是短转移
    jcxz(如果(cx)==0,转移到标号处执行)


    39、所有的循环指令都是短转移
    loop((cx)=(cx)-1,如果(cx)≠0)


    40、ret指令用栈中的数据,修改IP的内容,从而实现近转移;
    (ip)=((ss)*16+(sp))
    (sp)=(sp)+2
    相当于 pop IP
    retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移
    (ip)=((ss)*16+(sp))
    (sp)=(sp)+2
    (cs)=((ss)*16+(sp))
    (sp)=(sp)+2
    相当于 pop IP
    pop CS


    41、call将当前IP或CS和IP压入栈中,转移
    call 标号
    (1) (sp)=(sp)-2
    ((ss)*16+(sp))=(ip)
    (2) (ip)=(ip)+16位位移
    相当于 push IP
    jmp near ptr 标号
    call far ptr 标号
    (1) (sp)=(sp)-2
    ((ss)*16+(sp))=(cs)
    (sp)=(sp)-2
    ((ss)*16+(sp))=(ip)
    (2) (cs)=标号所在段的段地址
    (ip)=标号所在段的偏移地址
    相当于 push CS
    push IP
    jmp far ptr 标号
    call 16位reg
    (sp)=(sp)-2
    ((ss)*16+(sp))=(IP)
    (IP)=(16位reg)
    相当于 push IP
    jmp 16位reg
    call word ptr 内存单元地址
    (sp)=(sp)-2
    ((ss)*16+(sp))=(IP)
    (ip)=(内存单元地址)
    相当于 push IP
    jmp word ptr 内存单元地址
    call dword ptr 内存单元地址
    相当于 push CS
    push IP
    jmp dword ptr 内存单元地址


    42、在Debug中单步跟踪的结果,不能代表CPU的实际执行结果。

    43、mul乘法指令
    (1)两个相乘的数,要么都是8位,要么都是16位。如果是8位,一个默认放在AL中,另一个放在8位reg或内存字节单元中;如果是16位,一个默认在AX中,另一个放在16位reg或内存字单元中。
    (2)结果:如果是8位乘法,结果默认放在AX中;如果是16位乘法,结果高位默认在DX中存放,低位在AX中放。

    44、
    assume ds:data,cs:code ;实现求立方运算


    data segment
    dw 1,2,3,4,5,6,7,8
    dd 0,0,0,0,0,0,0,0
    data ends


    code segment
    start:   mov ax,data
        mov ds,ax
        mov si,0
        mov di,16
        mov cx,8

    s:     mov bx,[si]
        call cube ;调用带一个参数的函数cube
        mov [di],ax
        mov [di+2],dx
        add si,2
        add di,4

        loop s
        mov ax,4c00h
        int 21h

    cube:  mov ax,bx
        mov bx
        mov bx
        ret
    code ends


    end start


    45、寄存器冲突问题
    子程序的标准框架如下:
    子程序开始: 子程序中使用的寄存器入栈
    子程序内容
    子程序中使用的寄存器出栈
    返回(ret、retf)

    46、子程序的内部处理和显存的结构密切相关,但是向外提供了与显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的机构,为编程提供了方便。注意体会这种设计思想。

    47、8086CPU的标志寄存器有16位,其中存储的信息通常被称为程序状态字,PSW。

    48、flag的第6位是ZF,零标志位。它记录相关指令执行后,其结果是否为0。
    flag的第2位是pf,奇偶标志位。它记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数。
    flag的第7位是sf,符号标志位。它记录相关指令执行后,其结果是否为负。
    flag的第0位是cf,进位标志位。一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。
    flag的第11位是of,溢出标志位。一般情况下,of记录了有符号数运算的结果是否产生溢出进行记录。

    49、adc是带进位加法指令,它利用了cf位上记录的进位值。
    指令格式:adc 操作对象1,操作对象2
    功能:操作对象1 =操作对象1+操作对象2+cf

    50、sbb是带借位减法指令,它利用了cf位上记录的借位值。
    指令格式:sbb 操作对象1,操作对象2
    功能:操作对象1 =操作对象1-操作对象2-cf

    51、cmp是比较指令,cmp的功能相当于减法指令,只是不保存结果。cmp指令执行后,将对标志寄存器产生影响。

    52、flag的第10位是df,方向标志位。在串处理指令中,控制每次操作后,si、di的增减。df=0,每次操作后si、di递增;df=1,每次操作后si、di递减。

    53、串传送指令
    格式:movsb
    功能:

    (1)、((es)*16+(di))=((ds)*16+(si))
    (2)、如果df=0,则:(si)=(si)+1
       (di)=(di)+1
         如果df=1,则;(si)=(si)-1
       (di)=(di)-1
    当然也可以传送一个字,movsw,增量为2
    一般来说,movsb,movsw都和rep配合使用,用汇编语法来描述rep movsb的功能就是:
    s:movsb
       loop s
    可见,rep的作用是根据cx的值,重复执行后面的串传送指令。

    8086CPU提供下面两条指令对df位进行设置。
    cld指令:将标志寄存器的df位置0
    std指令:将标志寄存器的df位置1

    54、pushf的功能是将标志寄存器的值压栈,popf是从栈中弹出数据,送入标志寄存器中。

    55、用中断类型码,在中断向量表中找到中断处理程序的入口。找到这个入口地址的最终目的是用它设置CS和IP,使CPU执行中断处理程序。用中断类型码找到中断向量,并用它设置CS和IP,这个工作使用CPU的硬件自动完成的。CPU硬件完成这个工作的过程被称为中断过程。

    56、中断处理程序iret指令
    pop IP
    pop CS
    popf
    可以看到,在中断过程中,寄存器入栈的顺序是标志寄存器、CS、IP,而iret的出栈数序是IP、CS、标志寄存器。

    57、当TF=1时,CPU在执行完一条指令后将引发单步中断,转去执行中断处理程序。注意,中断处理程序也是由一条条指令组成的,如果在执行中断处理程序之前,TF=1,则CPU在执行完中断处理程序的第一条指令后,又要产生单步中断,则又要转去执行单步中断的中断处理程序,在执行完中断处理程序的第一条指令后,又要产生单步中断,则又要转去执行单步中断的中断处理程序。。。CPU当然不能让这种情况发生,解决的方法就是,在进入中断处理程序之前,设置TF=0。

    58、
    中断程序的安装
    assume cs:code


    code segment
    start: 设置es:di指向目的地址
        设置ds:si指向源地址
        设置cx为传输长度
        rep movsb

       设置中断向量表

       mov ax,4c00h
       int 21h

       中断处理程序
    code ends


    end start

    编写、安装中断7ch的中断例程
    功能:将一个全是字母,以0结尾的字符串,转换为大写
    参数:ds:si指向字符串的首地址

    主程序:
    assume cs:code,ds:data


    data segment
    db 'conversation',0
    data ends


    code segment
    start: mov ax,data
        mov ds,ax
        mov si,0
        int 7ch

        mov ax,4c00h
        int 21h
    code ends
    end start

    安装程序:
    assume cs:code


    code segment
    start:     mov ax,cs
            mov ds,ax
              mov si,offset capital
              mov ax,0
            mov es,ax
              mov di,200h
              mov cx,offset capitalend-offset capital
            cld
            rep movsb

          mov ax,0
          mov es,ax
          mov word ptr es:[7ch*4],200h
          mov word ptr es:[7ch*4+2],0
          mov ax,4c00h
          int 21h

    capital:   push cx
          push si
    change:    mov cl,[si]
          mov ch,0
          jcxz ok
          and byte ptr [si],11011111b
          inc si
          jmp short change
    ok:     pop si
          pop cx
          iret
    capitalend: nop

    code ends


    end start

    59、
    db "hello",'X',86
    分别定义了字符串,字符和数字

    60、CPU可以直接读写以下3个地方的数据
    (1)CPU内部的寄存器
    (2)内存单元
    (3)端口

    61、对端口的读写只有两个指令in、out
    注意,在in和out指令中,只能使用ax或al来存放从端口中读入的数据或要发送到端口中的数据。访问8位端口时用al,访问16位端口时用ax。

    62、shl和shr是逻辑移位指令
    shl是逻辑左移指令,它的功能为:
    (1)将一个寄存器或内存单元中的数据向左移位
    (2)将最后移出的一位写入CF中
    (3)最低位用0补充
    移动1位
    shl al,1
    移动位数大于1
    mov cl,3
    shl al,cl

    shr是逻辑右移指令,它和shl所进行的操作刚好相反

    63、在PC系统中,外中断源一共有以下两类
    1、可屏蔽中断
    CPU是否响应可屏蔽中断,要看标志寄存器的IF位的位置。IF=1,响应中断,IF=0,不响应可屏蔽中断

    (1)取中断类型码n
    (2)标志寄存器入栈,IF=0,TF=0
    (3)CS、IP入栈
    (4)(IP)=(N*4),(CS)=(N*4+2)

    8086CPU提供的设置IF的指令如下:
    sti,设置IF=1
    cli,设置IF=0
    2、不可屏蔽中断
    不可屏蔽中断是CPU必须响应的外中断。
    对于8086CPU,不可屏蔽中断的中断类型码固定为2,所以中断过程中,不需要取中断类型码。

    (1)标志寄存器入栈,IF=0,TF=0
    (2)CS、IP入栈
    (3)(IP)=(8),CS=(0AH)

    64、前面的课程中,我们一直使用标号来标记指令、数据、段的起始地址。比如:
    a: db 1,2,3,4,5,6,7,8
    b: dw 0
    但是,我们还可以使用一种标号,这种标号不但表示内存单元的地址,还表示了内存单元的长度,即表示在此标号处的单元,是一个字节单元,还是字单元,还是双字单元。
    a db 1,2,3,4,5,6,7,8
    b dw 0
    注意,标号a、b后面没有“:”,它们是同时描述内存地址和单元长度的标号。标号a,描述了地址code:0,和从这个地址开始,以后的内存单元都是字节单元;而标号b描述了地址code:8,和从这个地址开始,以后的内存单元都是字单元。
    因为这种标号包含了对单元长度的描述,所以,在指令中,它可以代表一个段中的内存单元。比如:

    指令:mov ax,b
    相当于:mov ax,cs:[8]

    指令:mov b,2
    相当于:mov word ptr cs:[8],2

    指令:inc b
    相当于:inc word ptr cs:[8]

    在这些指令中,标号b代表了一个内存单元,地址为code:8,长度为两个字节。

    一般来说,我们不在代码段中定义数据,而是将数据定义到其他段中。在其他段中,我们也可以使用数据标号来描述存储数据的单元的地址和长度。注意,在后面加有“:”的地址标号,只能在代码段中使用,不能在其他段中使用。

    下面的程序将data段中a标号处的8个数据累加,结果存储到b标号处的字中。
    assume cs:code,ds:data
    data segment
    a db 1,2,3,4,5,6,7,8
    b dw 0
    data ends
    code segment

    add b,ax

    code ends
    end
    注意,如果想在代码段中直接用数据标号访问数据,则需要用伪指令assume将标号所在的段和一个段寄存器联系起来。否则编译器在编译的时候,无法确定标号的段地址在哪一个寄存器中。当然,这种联系是编译器需要的,但绝对不是说,我们因为编译器的工作需要,用assume指令将段寄存器和某个段相联系,段寄存器中就会真的存放该段的地址。我们在程序中还要使用指令对段寄存器进行设置。

    指令:add b,ax
    编译为:add [8],ax

    65、直接定址表
    showsin: jmp short show
         table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180
         ag0 '0',0
         ag30 '0.5',0
         ag60 '0.866',0
         ag90 '1',0
         ag120 '0.866',0
         ag150 '0.5',0
         ag180 '0',0
    show:  push bx
           push es
         push si
         mov bx,0b800h
         mov es,bx

         mov ah,0
         mov bl,30
         div bl
         mov bl,al
         mov bh,0
         add bx,bx
         mov table[bx]

         mov si,160*12+40*2
    shows:  mov ah,cs:[bx]
         cmp ah,0
         je showret
         mov es:[si],ah
         inc bx
         add si,2
         jmp short shows
    showret:  pop si
         pop es
         pop bx
         ret

    本程序使用了两次直接定址表

    66、int9中断例程对键盘输入的处理
    按下a键,引发键盘中断;CPU执行int 9中断例程,从60h端口读出A键的通码,然后检测状态字节,看看是否有shift、ctrl等切换键按下;发现没有切换键按下,则将A键的扫描码1eh和对应的ascii码,即字幕‘a’的ascii码61h,写入键盘缓冲区。缓冲区的字节单元中,高位字节存储扫描码,低位字节存储ascii码。此时缓冲区的内容如下1E61

    按下左shift键,引发键盘中断,int 9中断例程接受左shift键的通码,设置0040:17处的状态字的第1位为1,表示左shift键按下。

    按下a键,引发键盘中断;CPU执行int 9中断例程,从60h端口读出a键的通码;检测状态字节,看看是否有切换键按下,发现左shift键被按下,则将a键的扫描码1EH和shiift_a对应的ascii码,即字母‘A’的ascii码41h,写入键盘缓冲区,此时缓冲区中的内容如下:
    1E61 1E41

    松开左shift键,引发键盘中断,int 9中断例程接受左shift键的断码,设置0040:17处的状态字节的第1位为0,表示左shift键松开。

  • 相关阅读:
    第34天-文件_system (2013.09.04)
    第33天-文件I/O _2(2013.09.03)
    小项目 : 计算库函数中单词的个数第30天
    第32天-文件I/O _1(2013.09.02)
    嵌入式培训学习历程第二十九天
    大作业 :学生信息管理系统。。。
    嵌入式培训学习历程第二十六天
    读取一个文件中哪一行 的一个参数
    LINUX C 语言 快速获取调用SHELL命令后的结果
    C语言制造一个随机数
  • 原文地址:https://www.cnblogs.com/luzhiyuan/p/3587470.html
Copyright © 2011-2022 走看看