1根底知识
机器语言是机器指令的调集,由0和1组成,可是很长很凌乱,汇编语言因而发生。
汇编语言的主体是汇编指令。汇编指令是机器指令的便于回忆的书写格式。
程序员写完汇编指令经过编译器转换为机器码,机器码再传到核算机履行。
汇编语言有以下三类:
1汇编指令:助记符,有对应机器码
2.伪指令:没有对应机器码,编译器履行核算机不履行
3.其他符号:+ -等由编译器识别,没有对应机器码
汇编语言的中心是汇编指令,决议了汇编语言的特性
CPU是核算机的中心部件,他操控整个核算机的运作并运算,指令和数据在存储器中寄存,也便是内存。CPU离不开内存。内存中指令和数据没差异,都是二进制。CPU来识别是信息还是指令。
一个存储单元存储1Byte
CPU从内存中读写书,要指定地址,指定进行哪种操作,CPU经过总线连接其他芯片,传输信息
存储单元的地址(地址信息)->地址总线
器材的选择,读或写的指令(操控信息)->操控总线
读或写的数据(数据信息)->数据总线
地址总线
一根导线有两种安稳状态代表0和1,那么10根导线就有2^10次方个不同数据,从0到1023
地址总线的宽度决议了CPU的寻址能力
数据总线
8根数据总线可传送一个8位二进制数据 8bits = 1byte
数据总线的宽度决议了CPU与其他器材进行数据传送时的一次数据量
操控总线
操控总线的宽度决议CPU对外部器材的操控能力
主板上都是中心器材,CPU、存储器等,CPU经过总线向接口卡发送指令,接口卡操控外设进行作业
随机存储器RAM 只读存储器ROM
BIOS(Basic input/putput system)
CPU将各类存储器看作一个逻辑存储器,一切的物理存储器被看作一个由若干个存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间。
内存地址空间的大小受CPU地址总线宽度的限制。不同核算机体系内存地址分配情况不同
2.寄存器
CPU由运算器、操控器、寄存器等器材构成,器材靠内部总线连,与之前总线(外)不同
寄存器程序员能够用指令读写
8086CPU的14个寄存器
AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW
通用寄存器:
用来寄存一般性数据 AX BX CX DX
16位寄存器能够拆成两个8位寄存器运用 AH AL,高8位和低8位
两字节byte构成一个字word,一个字节8bit放在8位寄存器
汇编指令
mov ax,18
mov ah,78
add ax,8
mov ax,bx
add ax,bx
…
16位寄存器只能寄存4位十六进制数据,1044CH最高位的1就不能保存再ax中004CH
独立运用AL这个寄存器如果进位是不会储存在AH中
数据传送和运算时 指令的两个操作方针的位数应当一致
物理地址
一切内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有仅有的地址
CPU向地址总线发出物理地址前,要在内部形成这个物理地址
16位的CPU能够一次性处理、传输、暂时存储16位地址
8086CPU有20位地址总线,内部用两个16位地址组成一个20位 的物理地址
物理地址 = 段地址 x 16(根底地址) + 偏移地址
段地址 x 16 常用说法左移4位(二进制位)相当于16进制左移1位
X 进制左移1位相当于乘 X
由于内部结构是这样,为了到达20位寻址能力,运用两个16位地址能够到达意图
接着段地址,内存没有分段,段的划分来自cpu
段地址 x 16是16的倍数,所以一个段的开端地址也是16的倍数;偏移地址位16位,16位地址的寻址能力位64kb,所以一个段的长度最大为64kb
CPU能够用不同的段地址和偏移地址形成同一个物理地址
8086的4个段寄存器CS、DS、SS、ES。当拜访内存由这四个段寄存器供给内存单元段地址
CS为代码段寄存器 IP为指令指针寄存器
任意时间,设CS内容为M,IP中内容为N,8086CPU从内存M x 16 + N单元开端,读取指令并履行
读取一条指令后,IP中的值主动增加(指令长度),以使CPU能够读取下一条指令
CPU将CS:IP指向的内存地址单元内容看作指令
同时修正CS、IP的内容能够用 jmp 段地址 :偏移地址
仅修正IP可用 jmp 某一合法寄存器(用寄存器中的值修正IP)?
8086机编程时,能够依据需求,将一组内存单元界说为一个段。
将长度为N<=64的代码,存在一组地址接连、开端地址为16的倍数的内存单元作代码段
用CS:IP指向的内容就能让代码段的内容履行
3.寄存器(内存拜访)
在内存中存储时,内存单元是一个字节byte单元,则一个字Word要用两个地址接连的内存单元来寄存,低位字节寄存在低地址单元,高位字节寄存在高地址单元
字单元:由两个地址接连的内存单元组成,开端地址为N的字单元简称为N地址字单元
0地址字单元4E21H,1地址字单元124EH……
DS和[address]
8086中有一个DS寄存器,通常用来寄存要拜访数据的段地址
mov bx,1000H
mov ds,bx
mov al,[0]
将10000H(1000:0)中的数据读到al中
这里[…]表明一个内存单元,括号里边表明偏移地址,指令履行时8086CPU主动读取ds中数据作为内存单元的段地址
8086CPU不支持将数据直接送入段寄存器,只好用一个寄存器中转
16位结构,有16根数线,所以一次能够传送16位数据,也便是一个字
mov add sub都是带有两个操作方针的指令,而jump具有一个
编程时依据需求界说数据段,能够在具体操作的时分用ds寄存数据段的段地址,再依据需求,用相关指令拜访数据段中的具体单元
栈
栈是一种具有特别拜访办法(最终进入空间的数据,最先出去)的存储空间
栈的两个根本操作:入栈和出栈,入栈便是将新的元素放到栈顶,出栈便是将栈顶元素取出一个
栈的操作规矩被称为:LIFO(Last In First Out,后的进先出来)
编程时,能够将一段内存当作栈
push ax 将ax中数据入栈
pop ax 从栈顶取出数据到ax
操作都是以字为单位进行的
8086CPU中任意时间,段寄存器SS(栈的段地址):寄存器SP(偏移地址)指向栈顶元素,push指令和pop指令履行时,CPU从SS和SP中得到栈顶地址
push ax两步走
1> SP=SP-2,SS:SP指向当时栈顶前面的单元,作为新栈顶
2> 将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶
入栈时,栈顶从高地址向低地址方向增长。
pop ax两步走
1> 将SS:SP指向的内存单元处数据送入ax
2> SP=SP+2,SS:SP指向当时栈顶下面的单元,作为新的栈顶
值得留意的时,出栈后pop操作前的栈顶元素依然存在,可是它现已不在栈中,再次push后会在那里写入新数据掩盖
由上面数据不在栈中进而能够思考栈顶超界的问题
8086CPU不保证咱们对栈的操作不会超界
当咱们把一段内存当作栈空间,当栈满时再履行push栈顶超出栈空间,栈空间外数据被覆当栈空时再次履行pop栈顶超出了栈空间,而超出的当地的数据会被掩盖,自己需求留意
用栈暂存今后需求康复寄存器中的内容时,出栈次序和入栈次序相反
push和pop实质上是一种内存传送指令
编程时依据需求可界说栈段
段的综述
关于数据段,将它的段地址放在DS中,用mov、add、sub等拜访内存单元的指令时,CPU就将咱们界说的数据段中内容当作数据来拜访
关于代码段,将它的段地址放在CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就将履行咱们界说的代码段中的指令
关于栈段,将它的地址放在SS中,将栈顶单元的偏移地址放在SP中,这样CPU进行栈操作时,将咱们界说的栈段当作栈空间用
由此可见CPU将内存中内容当作什么,是由于相应的段寄存器指向了那里
4.第一个程序
一个源程序从写出到履行的进程
第一步:编写汇编源程序
第二步:对源程序进行编译链接
运用汇编语言编译程序对源程序文件中的源程序进行编译,发生方针文件;再用链接程序对方针文件进行链接,生成可在操作体系中直接运转的可履行文件。
可履行文件包括两部分内容
1)程序(从源程序中的汇编指令翻译过来的机器码)和数据(源程序界说的数据)
2)相关的的描绘信息(比方,程序多大、占多少内存空间等)
第三步:履行可履行文件中的程序
操作体系依照可履行文件的描绘信息,将可履行文件中的机器码和数据载入内存,开端相关初始化,然后由CPU履行
源程序
汇编语言写的源程序,包括伪指令和汇编指令,其中伪指令由编译器来处理,程序是指源程序中由核算机履行、处理的指令或数据
伪指令:
1>segment 和 ends成对运用,功用是界说一个段,segment 阐明一个段开端,ends 阐明一个段完毕
格式: 段名 segment
.
段名 ends
2>end是一个汇编程序的完毕标志,编译器碰到它就完毕编译,留意区分ends
3>assume 意义为假定。它假定某一段寄存器和程序中的某一个用segment…ends界说的段相关联。
比方code segment … code ends就界说了一个名为code的段
在程序最初,用assume cs:code 将用作代码段的段code和CPU中的段寄存器cs 联系起来
DOS(一个单使命操作体系)
一个程序p2在可履行文件中,则必须有一个正在运转的程序p1,将p2从可履行文件中加载入内存,将CPU的操控权交给p2,p2才干运转。p2开端运转后,p1暂停运转
而当p2运转完,CPU操控权应交还给p1
这个进程叫做:程序返回
任何通用的操作体系,都要供给一个称为shell(外壳)的程序,用户运用这个程序来操作核算机体系进行作业
DOS启动时,先完结其他重要初始化作业,然后运转command.com,command.com运转后,履行完其他的相关使命后,在屏幕上显现出由当时盘符和当时途径组成的提示符
比方C:\
用户输入的指令 cd dir 等由command履行
履行一个程序,command 首先依据文件名找到可履行文件,然后将可履行文件加载入内存,设置CS:IP指向程序的进口。尔后,command 暂停运转,CPU运转程序。程序运转完毕后,返回到command中,command 再次显现由当时盘符和当时途径组成的提示符,等候用户输入
在DEBUG中,command将debug加载入内存,而debug将程序加载入内存,所以程序完毕后返回到debug中,Q能够返回到command
mov ax,4c00h
int 21h
这两条指令所完成的便是程序返回,在程序结尾运用
edit
编辑程序
masm
汇编编译器,接纳默许文件扩展名为 .asm,如果不是就要将文件扩展名写出
输入源程序文件名要指明途径,除非它就在当时途径下
masm 1t.asm / masm 1t
简化进程,最终加上 ; 疏忽中心文件的生成
link
链接器,接纳默许文件扩展名 .obj,如果不是就要将文件扩展名写出
对编译生成的方针文件进行链接,从而得到可履行程序。 输入方针文件名要指明途径,除非它就在当时途径下
link 1t.obj / link 1t
简化进程,最终加 ; 疏忽中心文件的生成
学习汇编首要意图,经过用汇编语言进行编程而深入地理解核算机底层的根本作业机理,到达能够随心所欲操控核算机的意图。汇编语言编程用到的东西在操作体系是运转,暂时不做过多探求
Debug
数据在Debug中默许一切数据用十六进制表明
遇到int 21h时要用P指令履行
载入.EXE
DOS体系中.EXE文件中程序加载,cx 中寄存了程序的长度
.exe装入内存后,程序被装入内存的什么当地?
5.[BX]和loop指令
1.bx
用[0]表明一个内存单元时,0表明单元的偏移地址,段地址默许在ds中,单元的长度(类型)能够由具体指令中的其他操作方针(比方寄存器)中指出
[bx]同样也表明一个内存单元,它的偏移地址在bx中
inc bx的意义是bx中的内容加1,履行后bx = 2
2.loop
正如它的意思循环
loop指令的格式是: loop 标号
CPU履行 loop指令时两步走(这里圆括号代表一个寄存器或内存单元的内容)
1>(cx)=(cx)-1
2>判别cx中的值,不为零则转至标号处履行程序,如果为零则向下履行
通常咱们用loop指令来完成循环功用,cx中寄存循环次数
程序框架如下
mov cx,循环次数
s:
循环履行的程序段
loop s
调试/履行程序时
大于9FFFh的十六进制数据A000H、A001H…FFFFH在书写时以字母最初,但在汇编源程序中,数据不能以字母最初,所以前面要加0,比方0A001H
在程序履行时 loop s 中的标号 s 现已变为了一个地址
咱们只想跟踪循环的进程时,能够用DUBUG里指令G来到达意图,一次履行完标号前的内容,g 0012表明履行程序到当时代码段(段地址在cs中)的0012h处
当进入循环后,咱们想要循环一次履行完,能够用p指令来到达意图,DEBUG就会主动重复履行循环中指令,直到(cx)=0停止
Debug和汇编编译器masm对指令不同处理
在Debug中,mov ax,[0] 表明将ds:0处的数据送入ax中
但在汇编源程序中,这个指令被编译器当作指令mov ax,0处理
Dubug将它解说为idata是一个内存单元
编译器将[idata]解说为 idata
现在的办法是将偏移地址送入bx寄存器中,用[bx]的办法来拜访内存单元
可是这样比较麻烦,还有一种办法是在[ ]的前面显式地给出段地址地点的段寄存器
段前缀
用于显式地指明内存单元的段地址的ds: cs: ss: es:,在汇编语言中成为段前缀
比方拜访2000:0单元
mov ax,2000h
mov ds,ax
mov al,ds:[0]
loop和[bx]的联合运用
在实践编程中,经常会遇到用同一种办法处理地址接连的内存单元中的数据问题。咱们需求每次循环的时分,依照同一种办法来改动要拜访的内存单元的地址
mov al,[bx] 中bx就能够看作一个代表内存单元地址的变量,咱们能够经过改动 bx 中的数值,改动拜访的内存单元
核算ffff:0ffff:b单元中的数据和,成果存储在ds中ffff:b中的数据累加到ds中,在这里边数据是8位的,不能直接加到16位寄存器dx中,也不能累加到dl中,dl会进位丢失
1.运算和的成果是字节型数据,范围在0-255之间,12个成果相加不会大于65535,dx能放下
2.不能将ffff:0
解决方案
用一个16位寄存器做中介,将内存单元中的8位数据赋值到一个16位寄存器ax中,再将ax中的数据加到dx上,从而使两个运算方针的类型匹配而且成果不会超界
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov bx,0
mov dx,0
mov cs,12
s:
mov al,[bx]
mov ah,0
add dx,ax
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
一段安全的空间
8086形式中,随意向一段空间写入内容很危险,这段空间可能寄存重要的体系数据或代码
dos和其他合法程序一般都不会运用0:200~0:2ff的256字节空间,运用这段空间是安全的,谨慎起见咱们还能够debug检查
6.包括多个段的程序
在代码段中运用数据
之前提到的那一段安全的空间只要256字节,咱们需求超过256个字节的空间该怎么办?在操作体系的环境中,合法地经过操作体系获得地空间都是安全的,由于操作体系不会让一个程序所用的空间和其他程序以及体系自己的空间相冲突。
程序获得所需空间的两种办法:
1.在加载程序的时分为程序分配
2.在程序履行的进程中向体系申请
咱们若要一个程序在被加载的时分获得所需的空间,则必须要在源程序中做出阐明
当可履行文件中的程序被加载入内存时,这些界说的数据同时也被加载入内存。与此同时,咱们要处理的数据自然而然地获得了存储空间
dw 0123h,”dw”的意义是界说字形数据即define word
编程核算0123h、0456h、0789h、0abch、0defh、0cbah、0987h的和,成果存在ax中
assume cs:code
code segment
dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h
start: mov bx,0
mov ax,0
mov cx,8
s: add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start
start这个标号在end后出现。伪指令end除了告诉编译器程序完毕外,还能够告诉编译器程序的进口在什么当地。end指令指明了程序的进口在标号start处
伪指令end描绘了程序的完毕和程序的进口。在编译、链接后,由”end start”指明的程序进口,被转化为一个进口地址,存储在可履行文件的描绘信息中
运用这种办法能够安排程序框架
assume cs:code
code segment
数据
start:
代码
code ends
end start
在代码段中运用栈
咱们需求栈空间,当然也要由体系分配,正如上面界说数据,数据就能载入内存。所以能够在程序中经过界说数据来获得一段空间,然后将这段空间作为栈空间运用
dw 0,0,0,0,0,0,0,0
之后合理设置栈顶ss:sp,这段空间就能够当作栈空间
所以描绘dw的作用时,能够说用它界说数据,也能够说用它拓荒内存空间
将数据、代码、栈放入不同空间
上述内容将他们放在一起程序显得混乱,用到栈空间也小,代码不长,放在一个段没问题(8086形式一个段的容量不能大于64kb)
所以考虑用多个段寄存数据、代码
assume cs:code,ds:data,ss:stack
data segment
dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,20h
mov ax,data
mov ds,ax
mov bx,0
mov cx,8
s: push [bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0: pop [bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
code ends
end start
界说多个段的办法和界说一个段办法相同
对段地址的引用:在程序中段名就相当于一个标号,它代表了段地址
咱们界说了三个段,作用如同的姓名意义,可是核算机不知道
咱们只需设置start的方位在code段(cs:ip),ss:sp指向栈顶,ds指向data段,其他寄存器如bx寄存data段中数据的偏移地址,即可依照咱们的意义分段了
7.更灵敏的定位内存地址的办法
and指令:逻辑与指令,按位进行与运算(对应位全1为1,否则为0)
例如指令
mov al,01100011B
and al, 00111011B
履行后al = 00100011B
经过该指令可将操作方针的相应位设位0,其他位不变
or指令:逻辑或指令,按位进行或运算(对应位有1为1,全0为0)
例如指令
mov al,01100011B
or al, 00111011B
履行后al = 01111011B
经过该指令可将操作方针的相应位设为1,其他位不变。
咱们要把人能看懂的信息存储在核算机中,就要对其及进行编码,将其转化为二进制信息进行存储。核算机要将存储的信息显现出来看就需求对其进行解码。
一个文本编辑进程中,就包括着依照ASCII编码规矩进行编码和解码:
键入”a”核算机用ASCII码的规矩对其进行编码,将转化为61H存储在内存指定的空间中;文本编辑软件从内存中取出61H,将其送到显卡的显存中;作业在文本形式下的显卡,用ASCII码的规矩解说显存中的数据,61H当作字符”a”,显卡驱动显现器,将字符”a”图画画在屏幕上
汇编程序中’……’的办法指明数据是以字符的形式给出的,编译器将其转换对应ASCII码
db ‘unIX’相当于db 75H,6EH,49H,58H
大小写转换问题
首先剖析:每个小写字母的ASCII码值比大写字母ASCII码值大20H,经过此办法能够将小写字母转化为大写,可这要先判别字母自身是否是大小写,但现在水平没有到达
再剖析:字母ASCII码的二进制,除第五位(从0开端计数)外,大写字母和小写字母其他各位都相同。大写字母第五位为0,小写字母第五位为1
因而能够用and 和 or指令将第五方位0或置1来改动大小写
[bx+idata]
[bx]能够指明一个内存单元,[bx+idata]更灵敏的指明内存单元,偏移地址(bx)+idata
段地址在ds中
db ‘BaSiC’
咱们要把这个字符串悉数转为大写,能够把这个字符串看作一个数组,首地址便是B
SI和DI
si和di是8086CPU和bx功用附近的寄存器,si和di不能分红两个8位寄存器来运用。
[bx+si]和[bx+di]
都表明一个内存单元,偏移地址是(bx)+(si),段地址在ds中
[bx+si+idata]和[bx+di+idata]
都表明一个内存单元,偏移地址位(bx)+(si)+idata,段地址在ds中
以上便是CPU供给的多种寻址办法
当咱们需求汇编中的循环嵌套时,能够将外层循环的cx数值保存起来,在履行外层循环loop指令前,再康复外层循环cx数值。
一般来说,在需求暂存数据的时分,咱们应该用栈
寻址办法的恰当运用,是咱们能够以更合理的结构来看待所要处理的数据。而为所要处理的看似凌乱的数据设计一种明晰的数据结构是程序设计的一个关键问题