作为一名自学计算机的学生,由于对计算机的底层原理非常感兴趣,所以找来了王爽老师的汇编语言来学习。感觉王爽老师的这本书真的是特别的基础,很适合学编程的像我这样的新人去看,仅以此篇博客作为学习中的经验。
纯正新手。。。有不对或不全面的地方求大佬轻喷
第一章 基础知识
第一章是本书的基础,通过学习基本了解了寄存器、机器语言、汇编语言的含义。
由于基础比较差,又特地在网上学习了二进制、四进制、十进制和十六进制之间的换算方法。二进制、四进制、十六进制转化为十进制的方法都是一样的,而十进制转化为其他进制的方法,通用的是短除法。
这里就不作详细说明,因为在电脑上不好打出来==
同时,为了学习汇编,又在电脑上安装了虚拟器VMware Workstation,以及PC2000的镜像。因为WIN10的系统没找到进入DEBUG的方法,且书中所用的系统为Windows2000,使用同样的环境更加方便学习。
第一章的内容是比较简单的,个人觉得理解一下就成,问题不是很大。
第二章 寄存器
第二章首先讲解了AX,BX,CX,DX四个寄存器,以及字节和字在寄存器中的存储方法,这些的理解在以后的学习中都会用到,所以一定要充分的进行理解。
接着讲解了一些简单的汇编指令。
再接着,是汇编中非常重要的内容,段地址和偏移地址的概念,此处需要熟练掌握。
再接着,是CS/IP的学习,以及修改寄存器的六个基础指令:
R命令:查看、修改CPU寄存器的内容;
D命令:查看内存中的内容;
E命令:改写内存中的内容;
U命令:将内存中的机器指令翻译成汇编指令;
T命令:执行一条机器指令;
A命令:以汇编指令的格式在内存中写入一条机器指令。
第三章 寄存器(内存访问)
前两章都是前些日子学习的了,因此没有刚学习的时候经验感悟那么多了,第三章是刚刚学习的,经验和感悟会相对多一些。
首先是字的存储,要搞清楚一个字分为两个字节,且字节存储时,高位内存单元存放高位字节,低位内存单位存放低位字节,在平时要能分清字单元和地址单元的区别。
在读写内存单元的时候,要用到的DS段寄存器,和[0]形式的偏移地址,用来提取内存单元中的数据(注意DS需要用其它寄存器进行存入)8086CPU不支持将数据直接送入段寄存器的操作。(ss、ds等都不支持)
MOV、ADD、SUB指令的内容。
栈的概念和机制:push和pop,出栈和入栈时CPU的运行机理,IP的变化情况。(push:SP=SP-2,录入数据;pop:读取数据,SP=SP+2)push ax:将ax中的数据入库;pop ax:将出库的数据放置到ax中。
其后的几小节多为加深理解的内容。理解这东西吧,因人而异,而且也很难系统的进行解释说明。
写几个学习过程中自己发现的点吧:ffff大小为64KB;CPU执行修改SS后的下一个指令会被自动执行
第四章 第一个程序
第四章中教会了我们制作我们的第一个汇编程序。
4.1 首先我们可以来一个总结,一个汇编程序从写出到执行的全部过程,如下(汇编程序需要使用masm和link程序进行编译和连接,在CSDN中可以找到这两个程):
编程(Edit/TXT)——1.asm/1.txt——编译(masm)——1.obj——连接(link)——1.exe——加载(command)——内存中的程序——运行(CPU)
我们先写出一个汇编程序的基本构成:
assume cs:codesg #将cs与codesg连接
codesg segment #程序开始
(内容) #有一个缩进,其中使用汇编语言编写内容
mov ax,4c00h
int 21h #程序返回
codesg ends #程序结束
end #结束
伪指令:
segment和ends是一对成对出现的伪指令,用来定义一个段;
assume表示“假设”,它假设某一段寄存器和程序中的某一个用segment和ends定义的段相关联;
end是一个汇编程序的结束标记。
codesg将被编译、连接程序处理为一个段的段地址。
4.2 编译中产生的列表文件(NUL.lst)和交叉引用文件(NUL.crf),以及连接中所产生的的印象文件(NUL.MAP),是不用理会的;而子程序(.LIB),由于在这里没有调用任何子程序,所以也不用理会。
#简单来说
NUL.xxx的不用理会直接ENTER
.xxx的可以进行命名,如果格式不同(asm和txt),需要把格式也写上
4.3 简化进行编译和连接时
masm C:\1.txt; #格式不同,加上txt;若位置不是默认的和MASM在一个文件夹,加上位置;注意分号
link 1; #obj文件,格式可省略;位置和LINK在一个文件夹,位置省略;注意分号
4.4 在DOS中,shell为command,由它对其他程序进行加载,运行程序时COMMAND会放弃对CPU的控制权;而DEBUG不会放弃控制权。
#使用DEBUG时注意:(1)在cmd页面中打出debug 1.exe的方法使用,而不是先进入DEBUG,再使用(我的这样会出BUG,不知道是个例还是都这样);
(2)cx中存放的为程序的长度;
(3)内存在运行程序时,会创建一个PSP数据区,长度为256个字节,100H,用来与被加载程序进行通信;
(4)因此,程序的地址为SA(DS中的数据)+10h:0;PSP的地址为SA:0
(5)执行到int 21h时,使用P指令,出现Program terminated normally字样;运行后使用Q指令退出debug;
(6)PSP的头两个字节为CD 20
附一篇 寄存器中CS,DS,SS,ES的区别(个人总感觉学这个的时候容易弄混)
首先 cpu中寄存器用于存储内存中数据的物理地址
cs 为代码段寄存器,一般用于存放代码;
通常和IP 使用用于处理下一条执行的代码
cs:IP
基地址:偏移地址
cs地址对应的数据 相当于c语言中的代码语句
ds 为数据段寄存器,一般用于存放数据;
ds地址对应的数据 相当于c语言中的全局变量
ss 为栈段寄存器,一般作为栈使用 和sp搭档;
ss地址对应的数据 相当于c语言中的局部变量
ss相当于堆栈段的首地址 sp相当于堆栈段的偏移地址
es 为扩展段寄存器;
第五章 [BX]和loop指令
5.1
[BX]表示一个内存单元,它的偏移地址在bx中,段地址在ds中。
loop表示循环,它的基本用法为:
mov cx,循环次数
s: 循环执行的程序段
loop s
5.2
(1)之所以有[BX]的存在,是因为在汇编源程序中,mov ax,[0] 会被编译为 mov ax,0。因此,我们需要BX对其进行中转。
inc bx #含义为bx的内容加1
(2)loop指令的格式是:loop标号,CPU执行loop指令的时候,要进行两步操作,a:(cx)=(cx)-1,b:判断cx中的值,不为零则转至标号s处执行程序,如果为0则向下执行。
#在汇编源程序中,数据不能以字母开头,所以要在前面加0.比如,9138h在汇编源程序中可以直接写为“9138h”,而A000h在汇编源程序中要写为“0A000h”
#其实loop在debug中执行时,先自动执行(cx)=(cx)-1,然后对cx进行判定,不为0就跳转IP为标号处的IP,原理是很简单的。
#当程序的前面部分的运行情况我们不需要了解时,可以使用g 指令跳转,如g 0012h表示执行程序到当前代码段(段地址在CS中)的0012h处。
#当需要循环的次数过多时,可使用P指令。其用法为,再遇到loop指令时,使用P指令,debug会自动重复循环中的指令,直到(cx)=0为止。
也可以直接使用g指令跳转,更为简单粗暴。
5.3 Debug和汇编编译器masm对指令的不同处理
在Debug中,
mov ax,[0]
表示将ds:0处的数据送入ax中;
但是在汇编源程序中,指令“mov ax,[0]”被编译器当做指令“mov ax,0”处理
Debug会将[idata]解释为一个内存单元;而编译器则将其解释为“idata”
若要在汇编源程序中也使用[idata],则要在[]前加上段寄存器。如ds:[0]
5.4 loop指令和[bx]和联合使用是我们最应该了解的,书上有详细解说,在这里不作介绍。
#“ds:”“cs:”“ss:”“es:”,在汇编中我们称为段前缀。
#在一般的PC机中,0:200——0:2ff的256个字节一般是空的。
实验四提示:(1)CS/code在这里都可以代表程序的开始
(2)程序的长度在CX中,mov ax,4c00h int 21h的长度为5
第六章 包含多个段的程序
dw:定义字形数据 。
start:用来表示开始运行代码的地方,和end start 配合使用 #这样看来,用其他字符也是可以的;专门设定一个开始的地方,是因为dw后的数据不需要被执行,如果被执行了反而会出错,所以干脆设定一个开始的地方。
在代码中使用栈时,可以用dw直接定义数据为0的空间,之后将这段空间作为栈使用。设置ss和sp即可。
为了表示的清晰,我们往往将数据、栈和代码放到不同的空间,这时,我们可以用 assume cs:code,ds:data,ss:stack这样的方法;使用时直接如mov ax,data使用即可,data在这里只是个地址。
注:mov ds,data是错误的,因为data会被编译器处理成一个表示段地址的数值。
CPU将哪里作为指令执行,只跟end 后跟的如start 有关,与assume中如何设定的无关。
如果一个段中的数据占n个字节,则程序加载后,该段实际占有的空间为(n/16+1)*16 /为取整。
开始时ds ss等都会是程序开始处的地址值,只有当执行了mov ss,mov ds后才会变为程序中需要的值。
db:定义单字节数据