汇编语言如何编程8086汇编语⾔学习(⼆)8086汇编开发环境搭建和Debug模式介
1. 8086汇编开发环境搭建
  在上篇博客中简单的介绍了8086汇编语⾔。⼯欲善其事,必先利其器,在8086汇编语⾔正式开始学习之前,先介绍⼀下如何搭建8086汇编的开发环境。
  汇编语⾔设计之初是⽤于在没有操作系统的裸机上直接操作硬件的,但对于⼤部分⼈来说,在8086裸机上直接进⾏编程将会⾯临各种困难。好在我们可以使⽤软件模拟器来模拟硬件进⾏8086的学习实践。在《汇编语⾔》中作者推荐通过windows环境下的masm和debug进⾏学习。
masm介绍:
  masm是⼀款DOS下的汇编⼯具包,在8086汇编的学习中我们需要其中的⼏个⽂件,分别是,。
  汇编器,⽤于将⽂本格式的汇编语⾔源⽂件编译为.obj结尾的⼆进制⽂件,其⽣成的.obj结尾的⼆进制⽬标⽂件是被编译的源⽂件的对应的机器码。单独的源程序⽬标⽂件通常是⽆法直接运⾏的,
还需要和互相依赖的其它同样编译完成的⼆进制⽂件链接在⼀起才能⽣成最终的可执⾏⽂件(⽐如所需要的静态库函数) 。因此,obj⽂件通常也被叫做中间⽂件。
  链接器,obj⽂件需要通过链接才能转换成可执⾏程序,⽽链接器就是负责完成这⼀任务的。链接器能将多个obj⽬标⽂件以及其所依赖的库程序进⾏统⼀处理(例如多个⽬标⽂件中指令、数据内存地址的偏移处理),并⽣成可执⾏⽂件。
debug介绍:
  调试器,windows提供了⼀个在dos中调试8086汇编程序的⼯具,提供了展⽰程序运⾏时CPU中各寄存器、内存中数据,指令级的单步调试等功能。debug程序的使⽤会在本篇博客的后半段进⾏详细介绍。
64位操作系统兼容性问题:
  由于《汇编语⾔》⼀书出版较早,当时的windows系统还是32位的,32位windows系统都默认安装了masm与debug,能打开dos窗⼝直接使⽤。但⽬前普遍使⽤的、新的windows 64位操作系统中却并没有默认提供masm⼯具包和,同时masm、debug也与64位的windows系统版本不兼容。
想在64位的windows系统下使⽤masm、debug有两个常⽤⽅法:
  1. 通过虚拟机安装⼀个⽼版本的windows操作系统(推荐windows xp)
  个⼈推荐第⼆种⽅法,下⾯介绍如何在windows64位操作系统下使⽤DOSBox来搭建8086汇编语⾔的开发环境。
DOSBox安装与使⽤
DOSBox下载安装:
  安装完毕后,到安装⽬录下的并启动,能看到如下图界⾯。
  作为dos的模拟器和普通的dos窗⼝没有明显区别,但是初始时并不能直接访问到本地磁盘,需要先将本地磁盘挂载到DOSBox中。
DOSBox挂载本地磁盘:
  1. 在本地操作系统磁盘上选择⼀个⽂件夹⽬录,作为挂载的磁盘路径(例如C:\dos)
  2. 在DOSBox启动的dos窗⼝中执⾏命令:mount C C:\dos(代表着将本地的C:\dos路径挂载到DOSBox的C盘路径下),能把dos窗⼝的⼯作⽬录切换到C盘,接下来就可以正常访问被挂载的磁盘路径下的内容了。
  3. 将前⾯提到过的等⽂件都放在这个挂载的本地磁盘路径下(例如C:\dos),通过DOSBox就可以兼容的运⾏masm⼯具包中的程序和了
添加⾃动执⾏脚本以避免重复操作:
  由于上述DOSBox的磁盘挂载是临时的,每次重新启动DOSBox后都需要重新输⼊命令进⾏挂载,太⿇烦了。我们可以通过修改DOSBox配置的⽅式,免去这些重复的操作。
  到DOSBox安装⽬录下的DOSBox 0.74 Options.bat,使⽤系统⾃带的记事本直接打开,暂不研究其它配置段的作⽤,到最后的【autoexec】段,配置在【autoexec】的内容会作为命令在DOSBox启动时按顺序被⾃动执⾏。 
  将挂载磁盘操作命令配置在【autoexec】段中能避免重复操作。修改并保存配置⽂件后,重新启动DOSBox,发现配置中添加的命令会被⾃动执⾏。
2. 8086debug模式介绍
  在搭建好了8086汇编的开发环境后,接下来介绍8086的debug模式。执⾏以进⼊debug调试模式,在dos中通过输⼊命令的⽅式进⾏交互。
  debug模式下有20多种不同命令,限于篇幅这⾥只会介绍⼏个以后实验时常⽤到的命令。(通过回车执⾏命令,DOS下的命令默认是不区分⼤⼩写的)
R命令查看/改变CPU寄存器内容
  R命令的作⽤是查看和修改debug模式下CPU中寄存器的值。
  (-r) 单独的输⼊r,可以查看当前CPU的内容
  (-r 寄存器名) r加上寄存器名可以在接下来的":"提⽰后输⼊新的值,以达到修改对应寄存器内容的⽬的(⽰例中第⼆⾏ AX 0000表⽰修改前寄存器AX的值为0000) 
D命令查看内存中的内容
  D命令的作⽤是查看内存中的内容。
  D命令有许多不同的传参⽅式可供使⽤,先介绍最易理解的(段地址:偏移地址)查看⽅式。D命令默认会显⽰寻址地址开始的后128个内存单元的内容,以16进制的⽅式显⽰(每个内存单元8位,⼀⾏最多16个内存单元),⽽最右边会将内存单元中的⼆进制数据以ascll码的形式翻译展⽰。
  有时,我们只想聚焦于某⼀部分内存地址的内容,⽽默认展⽰的内存视图不是很⽅便。
  D命令提供了另外⼀种访问内存的⽅式(段地址:偏移起始地址偏移终⽌地址),其能够展⽰(段地址:偏移起始地址⾄段地址:偏移终⽌地址)的内存信息,范围两端均为闭区间。
E命令改变内存中的内容
  E命令的作⽤是改变内存中的内容。
  和对CPU中寄存器的查看,修改不同,对内存进⾏查看和修改较为复杂,为此debug设计了两个不同的命令分别进⾏控制(E命令修改内存、D命令查看内存)。
  通过(E 起始地址数据1 数据2 数据3...)命令可以修改内存中以起始地址开始,顺序的N个内存单元的值(N为实际参数传递的数量)。
  也可以和R命令修改CPU中寄存器值类似的,通过提⽰来修改特定内存单元的值。00.12  00代表内存单元在修改前的值,12是我们⼿动输⼊的、需要修改的新值。
  可以通过E命令向内存输⼊对应的机器指令,因为机器指令也是数据的⼀种。
有以下指令(左侧为机器码,右侧为对应的汇编指令):
  B80100  mov ax,0001
  BB0200  mov bx,0002
  01D8      add ax,bx
  我们可以向内存1000:0处写⼊这些机器指令,以供接下来通过debug执⾏这段机器指令 (执⾏命令:E 1000:0 B8 01 00 BB 02 00 01
D8)。
U命令将内存数据转换为汇编指令展⽰
  U命令的作⽤是将内存中的⼆进制数据转换为汇编指令展⽰(反汇编)。
  D命令能够将内存中的数据以16进制或ascll码的形式展现出来,但有时我们需要观察的是内存中的机器指令时,D命令的视图过于抽象,不利于理解。debug提供了U命令来解决这个问题。
  对于前⾯我们在1000:0处输⼊的机器指令,使⽤ U 1000:0 命令(u 内存地址)可以将内存中的数据以汇编语⾔指令的⽅式进⾏展⽰。
  可以观察到,左边展⽰的是内存地址,中间则是16进制的内存视图,右边展⽰的是内存中数据所对应的汇编指令(例如: 1000:0000;B80100;MOV AX,0001)。
  由于我们只输⼊了三条汇编指令,⽽后⾯内存中的数据并不是我们想要执⾏的,但U命令却依然将其以汇编指令的形式转换并显⽰出来了。
  这也是前⼀篇博客所提到的,内存中的数据完全是⼆进制的,既可以将其看做普通的⼆进制数据、⼗六进制数据、ascll码⽂本数据,也可以视作程序指令,这些⼆进制的"数据"的处理完全取决于如何对其进⾏解释。
T命令单步执⾏机器指令
  T命令的作⽤是进⾏单步机器指令的调试
  以上⽂通过E命令写⼊内存1000:0的三条指令举例,介绍如何使⽤T命令来让CPU执⾏1000:0处的机器指令。T命令⽤于单步调试,⼀次只会执⾏⼀条机器指令。
  8086CPU在运⾏时会将CS:IP寄存器所指向的内存单元中的内容解释为指令执⾏,要将内存1000:0处的内容作为指令执⾏必须先修改CS、IP两个寄存器的值,使之指向1000:0。
  先执⾏⼀次T命令,1000:0处的指令(mov ax,0001)便会被执⾏,可以观察到寄存器ax的值已经变成了0001;同时寄存器IP的值增加了3(mov ax,0001的指令长度为3),此时CS:IP指向的便是位于1000:3处的下⼀条指令(mov bx,0002),在视图的最后⼀⾏中也有所体现。
  再执⾏⼀次T命令,会执⾏1000:3处的指令(mov bx,0002),可以观察到寄存器bx的值变成了0002;寄存器IP的值⼜增加了3(mov
bx,0002的指令长度也是3),此时CS:IP指向的便是位于1000:6处的下⼀条指令(add ax,bx)。
  最后执⾏⼀次T命令,add ax,bx会被执⾏(类似 ax=ax+bx)。寄存器ax的值已经变成了之前寄存器ax和bx中的数据之和0003;寄存器IP 的值增加了2(add ax,bx的指令长度是2),CS:IP指向1000:8。
A命令以汇编指令的形式向内存中写⼊内容
  A命令能够以汇编指令的形式向内存中写⼊内容
  对于内存操作,D命令可以查看内存中的内容,但如果想查看的是程序指令,显然U命令更加⽅便;E命令可以向内存中写⼊数据,但对于程序指令的写⼊,直接操作⼆进制机器码的⽅式过于硬核。为此,debug提供了A命令,我们可以通过A命令以汇编指令的形式向内存中写⼊内容。
  通过A命令将(mov ax,0001,mov bx,0002,add ax,bx)三条指令写⼊内存1000:0处:
  通过A命令进⾏指令的写⼊,和E命令达到的效果⼀样,但使⽤起来却更加便捷。A命令能够⾃动识别所输⼊汇编指令的长度,正确的在内存中写⼊程序指令。
  debug提供了D、E两种命令⽤于对内存进⾏通⽤的操作(纯⼆进制、⼗六进制数据的读、写)。
  对于程序指令,debug提供了U、A两种命令以更⼈性化的⽅式来读写内存中的指令内容。
3. 总结
  在debug模式下可以模拟8086汇编⾮常⾃由的控制CPU和内存,这也是汇编语⾔的强⼤之处和魅⼒所在。
贴近硬件底层的编程能够让我们编写出来的程序⾮常⾼效,但也存在⼀些问题:
  1.内存中的内容被当做指令还是数据来处理完全取决于如何解释,编程时稍有不慎就会导致CPU执⾏⼀些不应该执⾏的指令,甚⾄造成巨⼤的破坏。
  2.在未来还会介绍如何使⽤汇编语⾔来实现⾼级语⾔中出现的结构体、数组等概念。这些数据结构完全是程序逻辑上的,内存本⾝可没有这些功能。因此在使⽤汇编访问内存中结构化的数据时,⼀不⼩⼼就会出现内存访问越界,错位等问题。
  3.汇编语⾔的抽象程度过低,许多在⾼级语⾔中很简单的功能在汇编中也需要很多的代码来实现(汇编实现的控制台打印hello world可能是常⽤语⾔中最繁琐的了)。
  编程语⾔的贴近底层与机器⾼效性如果站在更⾼的⾓度上看其实是⼀把双刃剑:直接操控底层的机器⽅便,机器执⾏效率⾼的同时,也是危险、开发效率底下的。汇编语⾔程序员不得不付出巨⼤的精⼒来仔细思考、斟酌这些底层机器层⾯的细节,以避免出现相关bug,⼤⼤降低了开发效率。这也是⾼级语⾔诞⽣,并不断发展的主要原因。
  ⾼级语⾔⼤家族中按抽象程度来看,从偏底层的C,C++到java、python等,再到⽬前抽象程度最⾼的lisp。随着抽象程度的提⾼,离机器底层越远,执⾏效率通常也随之降低。但程序员所需要考虑的机器细节也就越少,能更专注于业务逻辑,进⽽提⾼了开发效率。⽐如在使⽤C编程时还需要仔细考虑指针错误,堆上⽆⽤内存回收等问题,到了更⾼级的java、python中,这些问题都交由编译器、虚拟机解决了,对开发⼈员也⼏乎透明了。
  天下没有免费的午餐,在选择适合的编程语⾔开发程序时,需要在机器执⾏效率和开发效率间做出取舍。但随着科学技术的发展,计算机硬件会越来越强⼤,对机器效率的担忧会越来越少,对程序开发效率的考虑将占据主导地位,越来越多的程序将会倾向于使⽤抽象程度更⾼的编程语⾔进⾏开发。
  虽然需要使⽤汇编语⾔的场合越来越少,但对汇编语⾔和底层机器硬件有⼀定的了解的话,依然能够帮助程序员更深刻的理解上层的知识内容、写出更⾼效的程序。
  毕竟,⼈类是⽆法抽象、封装到完美⽆缺的,有时还是你需要跳进下⽔道,深⼊底层⼀探究竟的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。