《汇编语言》学习笔记

 第一章 基础知识
 
现在的CPU有至少三种模式: 实模式、保护模式、虚拟8086模式。
在windows上打开cmd,进入的是虚拟8086模式。所以这时的CPU地址总线是20根,即至关于8086.
进入debug调试工具能够在DOS(实模式)下,或windows的cmd中(8086模式)。
 
另外,现在的32位系统规定内存中0-0X0000FFFF的64k空间保存给DOS使用。
 
 
 

第二章 寄存器(CPU工做原理)
 
8086全部的寄存器都是16位
其中【通用寄存器】是 AX BX CX DX
 
指令的操做对象位数必须是一致的,不然是错误的指令。
 
8086的地址线20位,内部是16位,所以是经过两个16位寄存器合成一个20位的物理地址。
物理地址 = 段地址*16 + 偏移地址 (乘以16即右移四位)
 
10H是8H的2倍,在16进制中。
 
内存并无分段,段的划分来自于CPU。
在编程时能够根据须要,将若干地址连续的内存单元看做一个段,用段地址*16定位段的起始地址,用偏移地址定位段中的内存单元。
注意1:段的起始地址必然是16的倍数。即最后一位是0.
注意2:偏移地址是16位,所以寻址能力,即一个段的最大长度为64KB.
注意3:CPU能够用不一样的段地址和偏移地址造成同一个物理地址。
 
【段寄存器】提供段地址
CS DS SS ES 
 
【CS】代码段寄存器,【IP】指令指针寄存器
8086中,任意时刻,CPU将CS:IP指向的内容看成指令执行。
 
第10节讲述了指令执行的详细过程。
 
实验一
【debug】
 
-r 查看、更改寄存器内容
-d 查看内存
-e 更改内存
-u 将内存中的机器指令翻译成汇编指令
-t 执行一条机器指令
-a 以汇编格式在内存中写入一条机器指令
-p 执行int 21h的时候用这个指令正常退出
-g 直接执行到某一地址(IP)
 
貌似个人环境中debug默认是16进制
 

第三章  寄存器(内存访问)
 
【mov】的用法:
mov 寄存器,数据   (段寄存器不能够)
mov 寄存器,寄存器
mov 寄存器,内存单元
mov 内存单元,寄存器
 
8086提供【栈】的设计。这意味着,咱们在基于8086CPU编程的时候,能够将一段内存看成栈来使用。8086的入栈和出栈操做都是以字为单位的。
入栈时,栈顶从高地址向低地址方向增加。
 
任意时刻,SS:SP指向栈顶元素。 
PUSH AX:
SP = SP - 2;
将 AX 中的内容送入 SS:SP 指向的内存单元中。
 
POP AX:
见 SS:SP 指向的内存单元的内容送入AX;
SP = SP + 2.
 
PUSH 和 POP 能够处理寄存器和内存单元。
 
8086没有防止栈越界的机制。
 


第四章  第一个程序
 
dos中是怎么加载启动程序的?
 
1.找到一段起始地址为SA:0的足够的内存。
 
2.前256个字节存放PSP(描述程序的前缀)。
 
3.从SA+10:0开始存放程序代码。
 
4.将内存区的段地址存入DS。DS=SA。
  设置CS和IP指向程序入口。即CS = SA+10.
 

第五章  [BX]和loop指令
 
在汇编源程序中,数据不能以字母开头。
 

第六章 多段程序
 
在实验5中发现,在程序中定义一个segment,最终得到的字节数会凑整到16的倍数。
 
程序开头的assume ...貌似没有做用,程序不会去对应。但assume cs: 貌似是必须的,不然程序会报错。
 
程序在编译后由操做系统选择一段足够大的内存空间装入。前256个字节是PSP, 即程序的一些描述信息,用于和操做系统的加载程序交流。
假如这段空间的起始地址是SA:0,则程序被加载后,默认:
DS=SA,
ES=SA,
SS=SA+10,
CS=SA+10+[code segment相对于源程序段的偏移位置/16].
例如,源程序:
assume cs:code,ds:data,ss:stack
data segment
dw 0                     ;虽然只定义了一个字,但data segment的长度将是16个字节。
data ends
 
stack segment
db 16 dup (0)
stack ends
 
code segment
...
code ends
end
 
加载后假设PSP起始地址是SA:0. 则:
DS =SA, 
ES=SA,
SS=SA+10,
CS=SA+11.
 
由以上能够看出,ds、es、ss的地址都是相对于SA固定的,assume彻底没有做用。要想改变只有用指令去赋值。而cs则有一个可变的偏移量,其值与assume的定义有关。
 
既然assume ds,es,ss是没用的,编程时就不必写了。经试验彻底可行。写这个彻底是为了便于理解。
 
另外,debug加载程序后能够发现,sp=0. 这说明若是不设置栈,程序会有一个默认的栈,栈顶与代码段的起始地址相同(这里的栈顶是指栈满时的位置),栈的大小为64k.

第七章   更灵活的定位内存地址的方法
 
大小字母转换
小写字母ASCII码的第五位为1,对应大写第五位为0.(位数从0开始数)
所以能够用and 和 or 指令。
 
寻址方式有如下几种:
1 [idata]
2 [bx]
3 [bx+idata]
4 [bx+si]
5 [bx+si+idata]
其中2和3中的bx能够被si或di代替。
 

第八章  数据处理的两个基本问题
这两个基本问题是:数据在哪?数据有多长?
 
总结一下8086的寄存器:
8086  CPU 中寄存器总共为 【14】 个,且均为 16 位 。 
即 AX,BX,CX,DX,SP,BP,SI,DI,IP,FLAG,CS,DS,SS,ES 共 14 个。
而这 14 个寄存器按照必定方式又分为了通用寄存器,控制寄存器和段寄存器。
 
【通用寄存器】:
AX,BX,CX,DX 称做为【数据寄存器】:
AX (Accumulator):累加寄存器,也称之为累加器;
BX (Base):基地址寄存器;
CX (Count):计数器寄存器; 
DX (Data):数据寄存器;
 
SP 和 BP 又称做为【指针寄存器】:
SP (Stack Pointer):堆栈指针寄存器; 
BP (Base Pointer):基指针寄存器;
 
SI 和 DI 又称做为【变址寄存器】:
SI (Source Index):源变址寄存器;
DI (Destination Index):目的变址寄存器;
 
【控制寄存器】: 
IP (Instruction Pointer):指令指针寄存器;
FLAG:标志寄存器;
 
【段寄存器】: 
CS (Code Segment):代码段寄存器;
DS (Data Segment):数据段寄存器;
SS (Stack Segment):堆栈段寄存器;
ES (Extra Segment):附加段寄存器;
 
【div指令】
格式: div reg
       div 内存单元  
       div后为除数。
1 除数有8位和16位,在某一寄存器或内存单元中。
2 被除数默认在AX或DX中。若是除数为8位,被除数则为16位,在AX中存放。
  若除数为16位,被除数则为32位,在DX和AX中存放,DX存放高16位。
3 结果:若是除数为8位,则AL存放商,AH存放余数。若是除数为16位,则AX存 
  商,DX存储余数。
 
【寻址方式小结】

寻址方式程序员

含义 (EA偏移地址,SA段地址)编程

名称windows

[idata]缓存

EA=idata; SA=(ds)工具

 

直接寻址oop

[bx]学习

[si]spa

[di]操作系统

[bp]翻译

EA=(bx); SA=(ds)

EA=(si); SA=(ds)

EA=(di); SA=(ds)

EA=(bp); SA=(ss)

 

寄存器间接寻址

[bx+idata]

[si+idata]

[di+idata]

[bp+idata]

EA=(bx)+idata; SA=(ds)

EA=(si)+idata; SA=(ds)

EA=(di)+idata; SA=(ds)

EA=(bp)+idata; SA=(ss)

 

寄存器相对寻址

[bx+si]

[bx+di]

[bp+si]

[bp+di]

EA=(bx)+(si); SA=(ds)

EA=(bx)+(di); SA=(ds)

EA=(bp)+(si); SA=(ss)

EA=(bp)+(di); SA=(ss)

 

基址变址寻址

[bx+si+idata]

[bx+di+idata]

[bp+si+idata]

[bp+di+idata]

EA=(bx)+(si)+idata; SA=(ds)

EA=(bx)+(di)+idata; SA=(ds)

EA=(bp)+(si)+idata; SA=(ss)

EA=(bp)+(di)+idata; SA=(ss)

 

相对基址变址寻址

另外,能够手动指定段基址寄存器。
 
【指令要处理的数据长度】
8086CPU的指令,能够处理两种长度的数据,byte和word. 因此在机器指令中必需要指明。有三种方法指明:
1 经过寄存器名
2 若没有寄存器名,用操做符X ptr指明内存单元的长度,X能够为word或byte.
3 有的指令默认处理字单元或字节单元。如push只进行字操做。
 

第九章 转移指令的原理
 
能够修改IP,或同时修改CS和IP的指令统称为转移指令。
  只修改IP时,称为段内转移,好比 jmp ax. 段内转移又分短转移和近转移。偏移量前者为8位,后者为    16位。所以前者的IP修改范围是-128~127,后者的修改范围是-32768~32767.
  同时修改CS和IP时,称为段间转移,好比 jmp 1000:0.
 
8086CPU的转移指令分为如下几类:
 无条件转移指令(如jmp)
 条件转移指令
 循环指令(如loop)
 过程
 中断
 
【jmp 指令】
1. jmp short 标号     翻译成机器指令是 EBXX,XX为8位的位移,范围是-128~127,为段内短转移。
2. jmp far ptr 标号   翻译成机器指令是 EAXXXXXXXX,包含CS和IP的目的地址,为段间转移。
3. jmp 寄存器           寄存器内容为IP目的地址,为段内近转移。
4. jmp word ptr 内存地址    内存的字内容为IP目的地址,为段内近转移。
5. jmp dword ptr 内存地址  内存的两个字内容分别为IP和CS地址,为段间转移。
 
在作实验9的时候,个人程序即便是对的,直接执行也看不到结果,而用debug一步步执行则看到显示缓存没有被改变。
解决方法: 进入debug  而后 -g 执行完的地址(通常是mov ax,4c00h)原理不知道。---貌似是由于中断。
 

第十章   call 和 ret 指令
 
call 指令至关于先push ip或者push cs, push ip,而后jmp ...
转移的方式与上一章中的jmp用法彻底同样。
 
ret指令相反,至关于 pop ip
retf则至关于 pop ip, pop cs
 
call 和 ret 配合使用则能够实现子程序机制。
 
【mul指令】
1. 两乘数同为8位或16位。若为8位,则一个默认在AH中,另外一个在8位寄存器或内存中;若为16位,则一个默认在AX中,另外一个在16位寄存器或内存中。
2. 结果:若为8位,则默认放在AX中。若为16位,则默认DX中存放高16位,AX中存放低16位。
格式为 mul 寄存器或内存单元。
 
为了解决子程序与主程序【寄存器冲突】的问题,能够这样编写子程序:
子程序开始:子程序中要使用的寄存器入栈
                       子程序内容
                       子程序中使用的寄存器出栈
                       返回(ret、retf)
 
由实验10.2得出的结论:
1. div可能会出现除法溢出错误
2. 在程序中有两次须要先保存ax的值,而后ax将用于别的运算。而空闲的寄存器不够,这时就用栈来帮忙。不过栈有出栈顺序的问题,这多是一个障碍。即若是你进栈以后又有别的数据进栈,此时你想出栈就得先把另外一个数据出栈。
 

第十一章  标志寄存器
 
标志寄存器 flag 中存储的信息一般称为程序状态字(PSW).
 
1. ZF,在flag的第6位,零标志位。相关指令执行后的结果如为0,则ZF=1。运算指令如add、sub等会影响ZF位,而传送指令如mov、push等指令则不影响。
2. PF,在flag的第2位,奇偶标志位。相关指令执行后的结果的全部二进制位中1的个数如为偶数,则PF=1.
3. SF,在flag的第7位,符号标志位。相关指令执行后的结果如为负,则SF=1。咱们看待数据能够是有符号数(补码表示),也能够是无符号数,这两种在计算机中的表示和计算都是同样的。所以计算指令必定会影响SF位,而咱们不必定须要这种影响。
4. CF,在flag的第0位,进位标志位。记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。CF进位针对的是无符号数。
5. OF,在flag的第11位,溢出标志位。好比8位寄存器,所能表示的范围是-128~127. 若是结果超出这个范围,则OF=1. OF位针对的是有符号数。
6. DF , 在flag的第10位,方向标志位。在串处理指令如movsb,movsw中,控制每次操做后si,di的增减。8086CPU提供两条指令对DF位进行设置: cld指令: 将DF置0;std指令:将DF置1.
 
要注意CF和OF的区别。CPU在执行add等指令的时候,包含两种含义:无符号数运算和有符号数运算。对于无符号数运算,CPU用CF位来记录是否产生了进位;对于有符号数运算,CPU用OF位来记录是否产生了溢出,以及用SF位来记录结果的符号。
 
adc,带进位加指令。格式: adc 对象1,对象2. 功能: 对象1=对象1+对象2+CF. 利用adc和add配合就能够对更大的数据进行加法运算。
sbb , 带借位减指令。原理与adc相同。
 
cmp , 比较指令。 格式:cmp 对象1,对象2. 功能: 计算【对象1-对象2】,但不保存结果,仅根据结果对flag进行设置。
 
条件转移指令。全部的条件转移指令的转移范围都是-128~127. 如je,jne,jb,jnb,ja,jna. 条件转移指令每每和jmp指令结合使用,但并非必须的。jmp指令只负责比较后设置相应flag位,条件转移指令则根据相应flag位决定是否跳转.
 
串传送指令: movsb,movsw. 前者一次传送一个字节,后者传送一个字  
movsb至关于 mov es:[di],byte ptr ds:[si]    ;8086并不支持这样的指令,这里只是个描述。
;若是DF=0
inc si  
inc di
;若是DF=1
dec si
dec di
通常来讲,movsb和movsw都和rep配合使用,格式: rep movsb;至关于:
s:    movsb
      loop s
 
pushf,将flag值压栈,popf,将flag值出栈。
 

第十二章  内中断
 
中断类型码为一个字节型数据,能够表示256种中断信息。产生中断信息的事件称为中断源。CPU内部有4种中断源:
1. 除法错误。如除法溢出。 中断码:0
2. 单步执行。中断码:1
3. 执行 int0 指令。中断码:4
4. 执行 int 指令。格式为int n , n 便是提供给CPU的中断类型码。
 
CPU用8位的中断类型码经过中断向量表找到相应的中断处理程序的入口地址。中断向量表就是中断向量的列表。而中断向量就是中断处理程序的入口地址。即,中断向量表就是中断处理程序的入口地址的列表。
8086规定中断向量表存放在内存的0000:0000到0000:03E8的1000个内存单元中。
 
中断过程:从响应中断到设置完CS和IP到中断向量,这个过程是由CPU硬件自动完成的,这个过程称为中断过程。
1 取得中断类型码N;
2 pushf
3 TF=0,IF=0
4 push CS
5 push IP
6 (IP)=(N*4), (CS)=(N*4+2)
以后,CPU开始执行由程序员编写的中断处理程序。
 
中断处理程序:
1 保存用到的寄存器
2 处理中断
3 恢复用到的寄存器
4 用iret指令返回
iret出栈的顺序与中断过程的入栈顺序相反。
 
--------------------------------------------------
因时间关系,看到这里对汇编语言的学习告一段落。
--------------------------------------------------
相关文章
相关标签/搜索