《汇编语言》(王爽)笔记

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键松开。

posted @ 2014-03-08 01:13  luzhiyuan  阅读(11664)  评论(0编辑  收藏  举报