汇编语言从零开始

来源:互联网 发布:it技术团队口号 编辑:程序博客网 时间:2024/06/05 09:01


--------------------------------------------------------------------------------
※ 对初学者而言,MASM 的功能太强了,它的许多命令太复杂, 以致常会妨碍了我们学习组合语言的兴致与效果。
※ 某些高明的指令事实上只对颇有经验的组合语言程式师有用,对我们而言,是太过高深了。
※ 事实上,使用 MASM 就好像在开 747 一样,只有 DEBUG 才有螺旋桨飞机的味道;因为,在 DEBUG 上面的控制全部都跟组合语言有直接的关系。
※ 如果您觉得组合语言蛮有趣的,终究您必须要用到 MASM。
※ 为了使学习组合语言程式有个好的开始,您必须要先排除那些华丽复杂的命令,将心力集中在最重要的几个命令上。
※ 随著您对 MASM 技巧的成长,最後您一定会感觉它是您在组合语言程式设计上的左右手。
※ 数字系统是电脑最常用的『思考模式』,也是我们和电脑沟通的最佳工具,您一定得认识它。
※ 同时,您也会知道中/英文内码的结构。
※ 硬体是个人电脑的躯壳,您绝对不能忽视硬体五大部门之间的关系。
※ 记忆体与中央处理的正确观念是组合语言必修的入门课程,您一定要熟悉它。
※ 作业系统模组化的完整概念是组合语言的基础,您一定要彻底了解它。
※ 在远古时代,人类就懂得使用『符号』来表示物体的数量。
※ 起初,这些符号只是口述的。
※ 例如∶一头长毛象、二块石头、三个人 ...。
※ 後来逐渐演化出较进步的数字系统,如: 结绳、画线 ...。
※ 随著人类文明的演进,人们所要计算的数量愈来愈大,发现这些方法不太方便。
※ 罗马人尝试以单独的符号来表示 1 以外的数值,例如∶ V代表 5,X 代表 10 等。
※ 虽然後来证明仍不切实际,但已为近代的数字系统,开启了新的方向。
※ 随著经验的累积,阿拉伯人归纳出了一些基本原则∶
1.某个数值以下的数以单独的符号表示。
2.超过该数值时,则在另一行重新开始。
3.使用一个特殊符号来表示『空』行。
※ 於是,阿拉伯人以人类的手指数,为分行的基准数值,创 造出了大家所熟悉的阿拉伯数字 0,1,2,3,4,5,6,7,8,9。
※ 在组合语言中,我们常在数值後面加上『d』,表示其为十进位(Decimal)数。
※ 由於人类有十根手指,所以藉由扳手指的便利,我们逐渐习惯了十进位的数字系统。
※ 上帝以其形体创造人类,同样的,人类也以自己的形体付与电脑生命。
※ 电脑本身也具有实体躯壳与思考的灵魂。
※ 电脑的躯壳就是我们常见的『硬体』,包含五大单元∶算术逻辑单元、输入单元 、控制单元 、输出单元、记忆单元。
※ 电脑的灵魂就是 MS-DOS,用来控制这五大部门的运作。
※ 『 组合语言 』就是用来撰写 MS-DOS 的基本语言。
※ 『中央处理单元,CPU』主要是指微处理机,是可以执行电脑所有算术/逻辑运算与基本 I/O 控制功能的单一晶片。
※ 组合语言与特定的微处理机晶片之间的关系牢不可分,也就是说,不同的微处理机其组合语言的指令语法亦不相同。
※ 个人电脑由 1981 年推出至今,其主要微处理机之发展过程为∶ 8088 → 80286 → 80386 → 80486。
※ 愈後面出来的微处理机,其功能愈强,指令亦愈复杂,但其必能包含前面微处理机的功能。
※ 为了确保您的组合语言程式可以适用於各种机型,所以仍以使用 8088 的组合语言,其相容性最佳。
※ 『 组合语言入门 』亦以解说 8088 组合语言为主。
※ 像人类的脑细胞一样,电脑使用IC(内含无数个电晶体)当『 主记忆体』 。
※ 记忆体是电脑运作中的关键成分,也是电脑在工作当中储存资讯的地方。
※ 主记忆体的组织有如许多可存放数值的储存位置,各以一个『位址』来识别。
※ 个人电脑所使用的储存位置是由『8 个位元』所组成,称作『位元组』,是个大小恰好可以放进一个字元的资讯单位。
※ 以一个组合语言程式设计师的观念而言,『记忆体』就是电脑内部可以用来存放『位元组』的地方。
※ 每一记忆体位置都有一个用来定位的位址。
※ 位址是一个数字,从零开始,往最高位址增加。
※ 由於位址是数字,电脑便能用其算术能力来计算与处理记忆体位置,而且各种电脑的设计都有其位址大小的限制。
※ 8088 系列的个人电脑采开放式的硬体架构,以『扩充槽』与其他周边装置互通讯息。
※ 每个扩充槽上有 62 条通道(I/O channel),其中分配 20 条给记忆体用。
※ 亦即,在 8088 中,位址有 20 个位元长,所以微处理机拥有达220的定址空间,相当於1M1024K,1K=1024)Byte,这也是 MS-DOS 的有效控制范围。
※ 大多数 8088 能做的算术都限於处理 16 位元的字元,其范围从2~216,即 0 到 64K。
※ 所以,必须用『分段式定址』的观念才能控制整个位址。
※ 完整的 20 位元位址可分成两部份,皆由 16 个位元组成∶
1、 区段部分(Segment)∶原来 16 个位元後面加上四个二进位0(一个 16 进位的0),变成 20 个位元,可设定至 1 M 中任何一个 64K 区段。
2、相对部份 (Offect)∶直接使用 16 个位元来接著区段部份,指向该区段中的任何一个位址。
※ 实际上,区段部份常成为相对部份能够定址 64K 工作区域 的一个『基础位址』。
※ 在组合语言中,您常会看到分段式定址写成∶『2222:3333』
※ 暂存器(Register)∶是电脑内部的一个实体元件,有点像记忆体的一个位址;不过,由於它是微处理机晶片的一部分,而不是记忆晶片的一部分,所以在暂存器之间的资料传送非常快速。
※ 8088 指令还可以在暂存器上做许多在记忆位置上无法做到 的事,例如∶
1.可将暂存器内的资料执行算术及逻辑运算。
2.存於暂存器内的位址可用来指向记忆体的某个位置。
3.暂存器可以用来读写资料到电脑的周边设备。
※ 8088 有 8 个 8 位元的『一般暂存器』,分别是∶ AH 与 AL,BH 与 BL,CH 与 CL,DH 与 DL。
※ 这些 8 位元暂存器可配对组成 16 位元暂存器∶
AHAL=AX『累加暂存器』常用於运算。
BHBL=BX『基底暂存器』常用於位址索引。
CHCL=CX『计数暂存器』常用於计数。
DHDL=DX『资料暂存器』常用於资料传递。
※ 为了运用所有的位址,8088 也设定了四个『区段暂存器』, 专门用来保存位址的区段部份∶
CS用於设定程式码区段(Code Segment)的位址。
DS用於设定资料区段(Data Segment)的位址。
SS用於设定堆叠区段(Stack Segment)的位址。
ES用於设定额外区段(Extra Segment)的位址。
※ 当一个程式要执行时,DOS 就要来决定程式码、资料和堆叠 各要用到那些位置,而设定区段暂存器 CS,DS,SS 来指向这些起始位置。
※ 通常程式语言处理机都是将『资料区段暂存器-DS』固定,而根据需要修改『程式码区段暂存器-CS』。
※ 所以,DOS 和『程式语言处理机』可以在可定址资料空间小 於 64K 的情况下,让程式可写成任意大小。
※ 一般而言,BASIC 直译器(Interpreter)就是程式处理机, 而您所认为的 BASIC 程式,其实只是其资料的一部分。
※ 所以,您的程式和其资料组合起来的大小,限制在 DS 所同  时涵盖的 64K 内。
※ 这就是一般 BASIC 程式和 COM 档案不得大於 64K 的原因。
※ 8088 以记忆体做为工作场所,却使用暂存器做为草稿纸,以 加速工作。
※ 除了前面所提的暂存器外,还有一些特殊功能的暂存器∶
IP指令指标(Intruction Pointer)暂存器
与 CS 配合使用,可追踪程式的执行过程。
SP堆叠指标(Stack Pointer)暂存器
与 SS 配合使用,可指向目前的堆叠位置。
BP基础指标(Base Pointer)暂存器
可用作 SS 区段的一个相对基础位置。
SI来源索引(Source Index)暂存器
可用来提供相对於 DS 区段之来源指标 。
DI目的索引(Destination Index)暂存器
可用来提供相对於 ES 区段之目的指标 。
※ 电脑必须能够对发生在其微处理机之外的事件有所反应。
※ 我们可以用两种方法来达到此目的∶
1.抽查法(pooling)∶电脑不断地寻找需要注意的事件,
因而花掉很多时间在检查工作上,故不被采用。
2.中断法(Interrupting)∶电脑静静地做自己的事,直到
事件引起它的注意为止。
※ 『中断法』使微处理机不会将时间浪费在寻找工作上,而是当有事件要完成时,工作自己会来找微处理机;这是电脑作 业有效率的主要因素。
※ 中断的主要观念是∶任何需要处理机照应的事,都以中断的
 形式来找处理机;依其功能的不同,可分成三类∶
1.『硬体中断』∶是由某种外部设备(例如∶磁碟机、键盘、
印表机等)要求照应而产生。
2.『逻辑中断』∶是由处理机本身在遭遇某些反常的情况(例
如∶除以零)所产生的。
3.『软体中断』∶用於一般大众用的服务,由 ROM-BIOS 及
DOS 所提供。
※ 『软体中断』对个人电脑之作业非常重要,是组合语言与其他高阶语言沟通所最常用到的服务。
※ 堆叠(STACK)就是给电脑以不互相干扰的方式,放置其工作。
 资料之处,与中断搭配,使电脑的效率及能力大增。
※ 堆叠的工作方式与自助餐馆中,所使用的餐盘架一样,放下 或取用盘子时,都由最上面开始,这就是『後进先出』原则。
※ 当电脑正忙著工作而接到一个中断时,需要一个地方来保存 正在做的记录,再去执行中断服务。
※ 当中断服务完成时,电脑也必须继续原来正在做的事情。
※ 堆叠在电脑中的工作方式是∶
1.有部份的记忆体(由高往低)供给堆叠储存体使用。
2.用特殊堆叠暂存器 SS 指向堆叠所在的记忆区域。
3.堆叠的顶端则以 SP 暂存器来指示。
※ 身为电脑中枢神经的 8088 其实只知道除了它本身以外的 三样东西∶
1.会打它头而要获得照应的外部中断。
2.与它交谈的记忆体。
3.传送资料进出记忆体以外任何地方唯一途径的埠(端口)。
※ 亦即,8088 是经由中断、记忆体的存取及埠来与外界沟通。
※ 埠(PORT)是 8088 微处理机用来替代和统一它与外界通讯 方式的机构。
※ 微处理机对於需要与之交谈的任何东西,例如∶键盘、磁碟机、喇叭等,都会给一个埠以供使用。
※ 电脑是靠硬体来工作的,但却需要靠思想来指导躯壳工作『软体』就是电脑的思想。
※ 软体是用来使电脑顺畅地运转,其中,『作业系统』是软体的最先锋,也是最重要、最复杂的程式。
※ 作业系统『DOS』所做的大部份工作,是把一些非常冗 繁杂的细节隐藏起来,使得我们得以轻易地使用电脑。
※ 以设计的眼光来看,DOS成功的关键之一是『模组化』
※ 当设计者将DOS要做的工作清楚地区别成各模组时,它被简化,而且效率更高。
※ 当然,这些模组必须组织成一个小心定义的阶层,其中各 层有其特定的工作,但却不必考虑其他阶层的工作细节。
※ 作业系统根据实际的需要,切割成六个主要模组∶
1.『ROM-BIOS』∶提供电脑所需基本和初步的服务。
2.『Boot Record』∶用以载入 DOS。
3.『IO.SYS』∶作为 ROM-BIOS 的可变扩充。
4.『MSDOS.SYS』∶提供核心的 DOS 服务。
5.『COMMAND.COM』∶处理您所键入的 DOS 命令。
6.『外部程式』∶用以提供特殊服务的程式档案。
※ 其中,MSDOS.SYS 为作业系统的『逻辑部份』,而 ROM-BIOS 及 IO.SYS 则构成『实体部份』。
※ 我们常用的磁碟作业系统『MS-DOS』,通常是指ROM-BIOS 以外的模组。
※ 我们可将整个作业系统想像成『 DOS 股份有限公司』,那系统的各部份就相当於公司内不同的『阶层』。
※ 『唯读记忆体中的基本输出入系统,ROM-BIOS』提供了某些最基本和初步的服务,相当於工厂中实际工作的工人。
※ ROM-BIOS 由制造厂商内建在 PC 中,是电脑的一个实体部 唯有修改系统的硬体才能改变它。
※ 通常 ROM-BIOS 所提供的服务(程式)主要是∶
1.打开电源时的自我测试程式。
2.启动并载入 DOS 的程式。
3.支援所有标准周边设备的程式。
※『启始录,Boot Recrod』相当於守卫,被放在系统磁片的 一个 RECORD ,用来载入 IO.SYS ,以起动 DOS 。
※ 由於启始录的大小是一个标准的磁区(Sector,512 Byte)所以,仅能存放一些固定的资讯。
※ 因此,系统磁片中,必须包含 IO.SYS 及 MSDOS.SYS 两个 档案,且要将它们放在一个标准且预先定好的位置。
※ 所以,您无法轻易地将普通的磁片变成系统磁片,因为这个特殊系统档保留的位置可能会被其它档案占用。
※ 由於启始录无法寻找这两个档案的位置,只能检查它们是 存在;故把它们设为隐藏档及系统档,以免被删除或擅改。
※ 启始录只有在系统档改变大小或位置时才必须改变,所以 您会在不同版本的 DOS 上发现不同的启始录。
※ IO.SYS 像领班一样,除了监督 ROM-BIOS 的动作外,必要可取代 ROM-BIOS ,以扩充其功能;当其他程式要使用 R -BIOS 的常式时,则必须经由 IO.SYS 来传送。
※ IO.SYS 具有 ROM-BIOS 无法做到的功能∶
1.可针对特殊 DOS 的特定需要而设定。
2.必要时,可修补任何 ROM-BIOS 中的错误。
3.可轻易地处理新的周边设备。
※ DOS 以『配置档,CONFIG.SYS』的方式,使得 PC 可以随 增加新的装置,让电脑的周边应用大放光彩。
※ DOS 将配置档中各『装置处理程式』当作 IO.SYS 的附加载入,产生了一种以模组的方式增加新的周边新装置,而干扰到 DOS 的系统档,这也是 DOS 观念的一大突破。
※ MSDOS.SYS 处理比较全面性的问题,如同公司的经理一样
※ MSDOS.SYS 包含输出入支援的 DOS 服务常式∶
1.『DOS 中断服务』∶用於直接取得控制 DOS 错误等作业。
2.『DOS 函数呼叫服务』∶用於间接提供的输出入服务。
※ 大多数的『 DOS 服务常式』常被较高层次的 DOS 程式所 用,当组合语言要执行任何输出入作业时,都会与它联系。
※ 其实,真正的输出入常式可能在 BOM-BIOS 中,但是程式 须要透过 MSDOS.SYS 才能使用它们。
※ 所有的 DOS 服务和 ROM-BIOS 服务,都故意设定成一种只由组合语言程式所使用的格式,以提高效率,这也是组合语言真正迷人的地方。
※ COMMAND.COM 负责控制作业系统的整个行动,可以说是作系统的决策部分。
※ COMMAND.COM 实际上分为三部份∶
1.『驻留部分,Resident Part』∶包括基本的控制功能以及错误处理常式,开机时与 IO.SYS 及 MSDOS.SYS 放在一起。
2.『开机部分』∶只有在 DOS 开机时,用来检查及执行一个AUTOEXEC.BAT 档,一旦完成,则舍弃此部份。
3.『暂存部分,Transient Part』∶包含了命令翻译器及执行 DOS 内部命令的程式,它们所占空间不少,若外部程式需较大空间时,DOS 会将此部份空间释放,以供程式使用。
※ 常程式执行後,COMMAND.COM 的驻留部份会先检查看看命翻译器是否有被盖掉,如果有,则再由 DOS 磁片中载入。
※ 这就是有时候您使用某些程式後,DOS 会要求您再放入磁片的原因。
※由於 DOS 的版权宣告及内部命令存放於 COMMAND.COM 档所以每家公司便可向 Microsoft 公司取得授权,而改成自独特的 DOS 版本,这也是 COMMAND.COM 独立的理由之一。
※当我们以组合语言写一个程式加以执行时,此程式就暂时管理COMMAND.COM 的工作,而且可以直接取用 MSDOS、IO、ROM-BIOS 的所有功能,COMMAND.COM 只有在程式结束时,才能重新取回控制权。
※ 那麽,是谁来告诉 COMMAND.COM 该做些什麽呢?就是您, 更精确的说,是您在提示符号 A> 之後所输入的命令。
※ 除了前面所谈的部分外,DOS 其馀的部分都是以『外部命令』的方式,置於磁片的程式档中。
※ 外部命令除了少部分(如∶FORMAT 等)外,大都非 DOS 之必须,所以,可视为辅助 DOS 的公用程式,而非作业系统本身。
※ 在撰写组合语言时,最常使用 DEBUG 及 EDLIN 等外部命令。
※ 编写组合语言有两种主要的方法∶
1.使用『组译程式∶MASM』。
2.使用『除错程式∶DEBUG』。
※ DEBUG 其实并不能算是一个『组译器程式,Assembly』,它的主要用途在於『除错』,即修正组合语言程式中的错误。
※ 不过,您也可以用 DEBUG 来编写短的组合语言程式,尤其对初学者而言,DEBUG 更是学习组合语言最佳的入门工具∶
『操作容易』
※ 利用 DEBUG 来键入与执行程式,只要呼叫 DEBUG 即可,过程简单。
※ 使用组译器时,必须用到文书编修程式、组译器本身、LINK、以及 EXE2BIN 等程式,其中每一个程式都必须用到一系列相当复杂的命令才能工作。
『额外列较少』
※ 真正的组合语言原始程式档案中,所出现的程式叙述,必须要在程式的开始与结尾设定一些额外的程式列,以供组译器所用。
※ 使用 DEBUG 可以避免一开始就碰到许多难以理解的程式列。
『较接近机器』
※ 使用 DEBUG 比使用组译器更能接触到电脑的实际运作情形。
※ DEBUG 所具的特性,可让我们触及电脑运作的最基本层次。
※ 以组合语言编写程式时,迟早必须了解此层次,与学习使用 DEBUG 以除错,所以先学习 DEBUG 似乎是一个好的开始。
『较适用於短程式』
※ 组译器有许多很强的特性,是组译大程式所不可或缺的。
※ 然而,对於初学者而言,DEBUG 已经够用了。
※ 使用 DEBUG ,您可以更专心於组合语言的探索,而不必被组译器那些复杂的特性吓著了。
『8088 的灵魂之窗』
※ 眼睛为灵魂之窗,DEBUG 可以说明 8088 的灵魂之窗。
※ DEBUG 除了能够组译程式之外,还可用来检查和修改记忆体位置、载入储存和执行程式、以及检查和修改暂存器。
※ 换句话说,DEBUG 是为了让我们接触 PC 的各种实体特性而设计的。
※ 当您将包含 DEBUG 的磁片插入磁碟机A後,从键盘就可启动DEBUG,DOS 的提示符号 A> 就是在等您的下一个指示。
※ 在本系统中,若提示『请输入 ...』,则在输入该内容後,要 再按 Enter 键,若提到『请按 ...』,则直接按该键即可。
1.请输入 DEBUG (记得加上 Enter 键)
2.出现在萤幕上的短线就是 DEBUG 的提示符号,表示 DEBUG 准备接受您的命令了。
※ DEBUG 的命令都是由『单一字母』来表示,後面通常跟著一个或数个数值。
※ D命令的作用是将记忆体的一部份倾印(Dump)在萤幕上。
※ 语法∶『D<启始位址>』
1.请输入 D100
记忆体位址     实际位元组资料相对的               ASCII 码
1FED:  0100 5F 5E 5D C2 02 00 01 00-00 00 0C 00 0D 00 0B 00    _^].............
1FED:    0110 0F 00 10 00 0E 00 BA 36-15 B9 04 00 E9 E5 F2 50     .......6.......P
1FED:    0120 B8 01 00 50 2B C0 50 E8-91 55 0B C0 79 08 B4 40     ...P+.P..U..y..@
1FED:    0130 E8 A7 2D F9 EB 04 E8 10-06 F8 C3 E8 89 FB 74 01  ..-...........t.
1FED:    0140 C3 B8 71 00 E9 61 2E A9-10 00 75 1A E8 78 FB BA  ..q..a....u..x..
1FED:    0150 00 00 C3 A1 A0 30 A3 86-30 B8 0D 00 E9 1C 2E 83   .....0..0.......
1FED:    0160 3E 12 2F 3C 73 ED 57 FF-36 12 2F B8 27 00 E8 D3   >./<s.W.6./.'...
1FED:    0170 0D BF 25 00 74 0D 2B FF-B8 C2 00 E8 C6 0D 75 06  ..%.t.+.......u.
※ 画面上的每一对数值『两个十六进位数字,00h~FFh』都代表着某个忆体位置中储存的『一个位元组资料』。
※ 每一列有 16 对数值,中间以短线将左、右 8 个位元组隔开,以便容易阅读。
※ 想不想改变记忆体的内容?最简单的方法就是使用F命令。
※ F命令可在记忆体中填入(Fill)一个特定的十六进位数。
※ 语法∶『F<启始位址> <结束位址> <填入值>』
1.首先,请输入 F100 17F 00
2.然後,再输入 D100
 
1FED:0100 5F 5E 5D C2 02 00 01 00-00 00 0C 00 0D 00 0B 00 _^].............
1FED:0110 0F 00 10 00 0E 00 BA 36-15 B9 04 00 E9 E5 F2 50 .......6.......P
1FED:0120 B8 01 00 50 2B C0 50 E8-91 55 0B C0 79 08 B4 40 ...P+.P..U..y..@
1FED:0130 E8 A7 2D F9 EB 04 E8 10-06 F8 C3 E8 89 FB 74 01 ..-...........t.
1FED:0140 C3 B8 71 00 E9 61 2E A9-10 00 75 1A E8 78 FB BA ..q..a....u..x..
1FED:0150 00 00 C3 A1 A0 30 A3 86-30 B8 0D 00 E9 1C 2E 83 .....0..0.......
1FED:0160 3E 12 2F 3C 73 ED 57 FF-36 12 2F B8 27 00 E8 D3 >./<s.W.6./.'...
1FED:0170 0D BF 25 00 74 0D 2B FF-B8 C2 00 E8 C6 0D 75 06 ..%.t.+.......u.
-F100 17F 00
-D100
1FED:0100 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

3.记忆位置 100 到 17F 现在都已填入 00,这正是F命令所指定的。
※ E命令是用来输入(Enter)一串位元组的资料到记忆体中, 以构成程式。
※ 要将此程式插入记忆体中,只要在E命令後面跟著程式所要
 存放的位址即可。
1.首先,请输入 E100
2.然後,再输入 B2 01 B4 02 CD 21 CD 20
3.现在您已将程式放入记忆体中了,请输入 G
4.画面上将出现此程式执行的结果。
Program terminated normally
※ 由於机器码是给电脑看的,当然不适合用来写程式。
※ 我们可以使用组合语言指令直接来编写程式,A命令就是用来将组合语言指令『译,Assemble』成十六进位数值的机器码。
※ 以 DEBUG 编写的程式一定要由位址 100h 开始才有效。
1.请依序输入 A100
2.请依序输入 MOV DL,1
3.请依序输入 MOV AH,2
4.请依序输入 INT 21
5.请依序输入 INT 20
6.请按 Enter 键
7.现在您已经将组合语言程式放入记忆体中了,请输入 G
8.画面上所出现的结果将与用E命令编写的程式一样。
Program terminated normally
9.请输入 D100
10.位址 100 到 107 就是程式的内容,与E编写的机器码一样。
1FED:0100 B2 01 B4 02 CD 21 CD 20-00 00 00 00 00 00 00 00 .....!. ........
1FED:0110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
※ 我们可以用U命令将十六进位的机器码反组译(Unassemble) 成组合语言指令,语法∶『U<启始位址>,<终止位址>』
※ 您将发现∶每一行右边的组合语言指令就是被组译成中间相 对的机器码,而 8088 实际上就是以机器码来执行程式。
1.请输入 U100,106
1FED:0100 B201 MOV DL,01
1FED:0102 B402 MOV AH,02
1FED:0104 CD21 INT 21
1FED:0106 CD20 INT 20
MOV DL,01
MOV AH,02
INT 21
INT 20
※ MOV A,B 将 B 的内容移(MOVE)到 A 中。
※ MOV DL,01 将数值 01h 移入 DL 暂存器
※ MOV AH,01 将数值 01h 移入 DL 暂存器
※ DOS 功能服务是存在於 ROM-BIOS 与 IO.SYS 内的输出入常式,我们可以利用 INT 21 来呼叫,其过程为∶中断要求先被送到 MSDOS.SYS 部份,依 AH 的值,转送到 IO.SYS 或 ROM-BIOS 中的适当常式。
※ INT∶呼叫由中断(Interrupt)向量所指的常式。
※ 显示器输出功能∶DOS 的 02h 号函数服务。
※ 输入∶AH=2DL=字元之对应数值
※ 执行∶INT21结果∶字元显示在萤幕上
※ INT 21∶DOS 的中断服务,由函数呼叫使用,程式可透过此中断来呼叫所有的 DOS 函数服务常式。
※ INT 20: DOS 的中断服务,用来终止程式作业,使程式执行後,将控制权交回给 DEBUG,并出现 Program terminated normally 讯息。
※ DEBUG 中可以用R命令来查看 8088 中的暂存器(Register) ,也可以用来改变暂存器的内容。
※ 您还记得每个暂存器所代表的意义吗?
※ 请注意 IP 暂存器,它保存了目前将执行的指令位址。
1.请输入 R
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1FED ES=1FED SS=1FED CS=1FED IP=0100 NV UP EI PL NZ NA PO NC
1FED:0100 B201 MOV DL,01
-
1.当程式由 100 开始执行,且以 INT 20 指令结束时,DEBUG 会自动将 IP 的内容更新设定为 100。
※ 当您要将此程式做成一个独立的外部程式,以便在 DOS 的提示符号下随时都可以执行,则必须用N命令对该程式命名。
※ 程式档案一定要为 COM ,否则下次无法以 DEBUG 载入。
※ 语法∶N<程式名称>.COM
1.请输入 NSMILE.COM
※ 接著,您必须告诉 DEBUG 此程式有多长∶程式从 100 开始到106,所以实际占用 7 个位元组。
※ 我们利用 BX 暂存器 存放此值高位元组部分,而以 CX 暂存器存放此值的低位元组部分。
2.请输入 RBX (查看 BX 暂存器的内容)
3.因 BX 已设定为 0,请按 Enter 键
4.请输入 RCX(查看 CX 暂存器的内容)
5.请输入 7(程式的位元组个数)
※ 最後,请用W命令将该程式写入(Write)磁片中。
6.请输入 W
※ 当我们在写组合语言程式的时候,通常不会直接将十六进位的 位元组(机器码)放入记忆体中,而是打入一串『助忆符号』(Mnemonic Symbols),这些符号比十六进位数更容易记住。
※ 助忆符号是以2或3个字元的名称来代表某个组合语言指令,以告诉微处理机应执行何种运算。
※ 也就是说,助忆符号所构成的组合语言是为人类而设计的, 而机器语言是针对 PC 的特性而设计的。
※ 事实上,8088 微处理机真正所能读取和了解的并非十六进位
数值,而是二进位数或是位元形式;十六进位数只是使人类更易了解二进位数字的一种表示方式而已。
※ 还记得 ASCII 码?现在,我们来剖析一个可以将所有的 ASCII码显示在萤幕上的程式。
※ 请将存在磁碟中的程式 ASCII.COM 载入并执行。
1.请输入 DEBUG ASCII.COM
2.请输入 U100,10E
1FED:0100 B90001 MOV CX,0100
1FED:0103 B200 MOV DL,00
1FED:0105 B402 MOV AH,02
1FED:0107 CD21 INT 21
1FED:0109 FEC2 INC DL
1FED:010B E2F8 LOOP 0105
1FED:010D CD20 INT 20
-
※ INC∶递增指令,每次将资料暂存器 DL 内的数值加 1 。
※ LOOP∶回圈指令,将执行次数放入计数暂存器 CX 中,每执行一次 LOOP 指令,CX 的内容值减 1 ,并跳回回圈的开头位址(105),直到 CX 为 0 。
1.请输入 G
※ 当我们想在萤幕上任意地显示字串(例如∶开机时电脑会向您问侯),则可以使用 DOS 的 9H 号函数服务常式。
※ 请输入下列程式,存入磁碟(HALLO.COM)中,并执行看看∶
1.请输入 A100
2.请输入 MOV DX,109
3.请输入 MOV AH,9
4.请输入 INT 21
5.请输入 INT 20
6.请输入 DB 'HOW ARE YOU, SU$'
※ 列印字串功能∶DOS 的 09h 号函数服务。
※ 输入∶AH=9DS:DX = 字串的启始位址
※ 执行∶INT21结果∶字串显示在萤幕上
DX∶字串的偏移位址,即实际的启始位址。
DS∶字串的段位址,DEBUG 会自动检查其值,目前不必管它。
※ 在组合语言中,有两种不同的指令∶
1.正规指令∶如 MOV 等,是属於微处理机的指令,用来告诉 8088微处理机在程式执行时应做些什麽,所以它会以运算码(OP-code)的方式存入记忆体中。
2.虚拟指令∶如 DB 等,是属於 DEBUG 等组译器的指令,用来告诉组译器在组译程式时应做些什麽。
※ DB(Define Byte)指令用来告诉 DEBUG 将其後面单引号内的所有字元对应的位元组(ASCII 码)放入记忆体中。
※ 使用 9H 功能列印的字串必须以 $ 符号结尾。
※ 想查看 DB 虚拟指令将那些位元组放入记忆体时,使用U命令并没有太大的用处,此时改用D命令则可得到较佳的效果。
5.请输入 U100,118
6.请输入 D100,17F
1FED:0100 BA0901 MOV DX,0109
1FED:0103 B409 MOV AH,09
1FED:0105 CD21 INT 21
1FED:0107 CD20 INT 20
1FED:0109 48 DEC AX
1FED:010A 6F DB 6F
1FED:010B 7720 JA 012D
1FED:010D 61 DB 61
1FED:010E 7265 JB 0175
1FED:0110 20796F AND [BX+DI+6F],BH
1FED:0113 752C JNZ 0141
1FED:0115 205355 AND [BP+DI+55],DL
1FED:0118 2400 AND AL,00
-

1FED:0100 BA 09 01 B4 09 CD 21 CD-20 48 6F 77 20 61 72 65 ......!. How are
1FED:0110 20 79 6F 75 2C 20 53 55-24 00 00 00 00 00 00 00 you, SU$.......
1FED:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1FED-
※ 现在,我们来剖析一个更见弹性的程式∶可以由键盘输入任意字串,然後再忠实地显示一次。
※ 请将存在磁碟中的程式 MIRROR.COM 载入并执行。
1.请输入 NMIRROR.COM 3.请输入 G
2.请输入 L 4.请输入 THIS IS MY PROGRAM$
※ 提供缓冲式键盘输入∶DOS 的 0Ah 号函数服务。
※ 输入∶AH=ADS:DX = 缓冲区位址
※ 执行∶INT21结果∶键盘字元存於缓冲区内
※ 缓冲区实际上就是一系列的记忆位置,其大小由 db 所定义。
※ 本程式 db 20 指示 DEBUG 保留 20h 个未用的记忆体位置供缓冲区使用,并将值 20h 存入第一个位元组(116)中。
※ 当您输入字串并按 Enter 键後,DOS 的 0Ah 号功能会将真正输入的字元个数存第二个位元组(117)中。

1FED:0100 MOV DX,0116
1FED:0103 MOV AH,0A
1FED:0105 INT 21
1FED:0107 MOV DL,0A
1FED:0109 MOV AH,02
1FED:010B INT 21
1FED:010D MOV DX,0118
1FED:0110 MOV AH,09
1FED:0112 INT 21
1FED:0114 INT 20
1FED:0116 DB 20
※ 由於缓冲式键盘输入功能 Ah 在每个字串最後都会印一个归位字元(由 Enter 键产生),使得游标会自动回到输入列的最前端。
※ 为了使萤幕映射输出的字串不会盖掉原来输入的字串,所以利用功能 2h 列印一个换列字元(OAh),使得游标移到下一列的最前端。
※ 列印字串功能 9h 的 DX 暂存器要保存字串的启始位置。
※ 牢记∶9h 功能只有遇到 $ 符号时才会停止输出字元,因此您打入字串的最後必须加上 $ 符号,否则 9h 功能会继续将记忆体中的无用资料胡乱地列印出来。
※ 『组译器,Assembler』可将符号指令转换成机器语言。
※ 『机器语言』是真正存在 PC 记忆体,而由 8088 处理机执行的二进位数值。
※ 由二进位数值组成的程式,能以 COM 的档案形式存於磁片上。
※ 撰写一个较长的组合语言程式时,若我们要逐一翻阅 8088参考手册,将每个指令组合成目的码,则您将不会觉得学习组合语言是件有意义的事。
※ 然而,机器语言程式规划的过程中,最无法和人类的天份配
合,但却又是与电脑最能配合的阶段,便是『组合』本身。
※ 『工欲善其事,必先利其器』,Micrsoft MASM 是 PC 最佳 的组译器,可自动将组合语言转换为机器语言。
※ 与其说 DEBUG 类似组译器,倒不如说它较类似直译器更贴切。
※ DEBUG 的 A 命令可将每一个符号指令,逐一转成机器语言,存於记忆体中,且立刻执行。
※ 真正组译器(MASM)的运作是利用文书处理程式(如∶EDLIN、PE Ⅱ等)将符号指令建成一个完全独立且附属档名为 .ASM 的文字档案。
※ 此文字档案又称『原始码档案』,是 MASM 程式的输入部分。
※ MASM 将输入的 ASM 档案,组译且建成一个 .OBJ 的磁碟档案,称为『目的码档案,OBJet』。
※ OBJ 档案仅包含有关程式各部份要载入何处及如何与其他程式合并的各项资讯,无法直接载入记忆体执行。
※ 『链结程式,LINK』可帮我们将 OBJ 档案转换成可载入记忆体执行(EXEcute)的 EXE 档案。
※ 您还可以用『EXE2BIN,EXEcute TO BINary』档案,将 EXE 档案转成 COM (COMmand)档案。
※ DOS 的 COMMAND.COM 在执行外部命令时,是直接在适当的磁片中寻找一个命令处置档,并加以执行。
※ 命令处置档有三类,以其附属档名来区别;COMMAND.COM 每次都是以固定的顺序寻找,其优先顺序为 COM → EXE → BAT。
※ COM 档案只由程式所需的二进位数值所组成,是储存程式最简单的方式,不但占用的记忆体最少,而且载入速度最快。
※ EXE 档案除了二进位数值外,同时还包含一个由档案有关的各种资讯所组成的『档头,Header』,所以档案较大。
※ COM 档案可直接利用 INT 20 返回 DEBUG,而 EXE 档案则需要较复杂的返回程序。
※ 载入 COM 档案时,无法与其他档案键结起来,所以不能产生记忆体空间超过 64K 的 COM 档案;而 EXE 档案则较易利用不同的记忆段来执行不同的功能,所以其大小可超过 64K Bytes。
※ 由於初学的程式不大,且为了完全了解所有的过程,所以我们以 COM 档案为第一个组合语言程式的形式。
※ 利用文书处理程式建立一个 SMILE.ASM 的原始码档案。
※ ASM 档案中的空白行可帮助您分清楚程式的架构,便於阅读。
※ MASM 会将所有的数值假设为十进位,而 DEBUG 则使用十六进位;所以在原始码中,我们必须加上h,以代表十六进位。
※ 若是以字母开头的十六进位数字,则还必须在字母前加个 0,以表示它是数字。
原始程式码 DEBUG 程式
prognam segment
assume cs:prognam
mov dl,1 mov dl,1
mov ah,2 mov ah,2
int 21h int 21
int 20h int 20
prognam ends
end

※ 原始程式码新增了四列叙述∶
※ prognam segment 与 prognam ends 是成对的,用来告诉 MASM 及LINK,此程式将放在一个称为『PROGNAM』(PROGram NAMe) 的『逻辑区段』内;其中区段名称(PROGNAM)可以任取,但其位置必须固定,且两个区段名称必须相同。
※ assume cs:prognam 必须在程式的开头,用来告诉组译器,此程式所在区段的位置放在 CS 暂存器中。 ※ end 用来告诉 MASM,程式到此结束。
※ 要产生 COM 档案的所有原始程式码都需含这四列,且必须依相同的次序及位置出现。
※ 以 MASM.EXE 组译 ASM 档案时,不可打入附属档名 ASM。
※ 组译器会要求您输入 OBJ 档案、表例(LST)档案及对照(CRF)档案(供除错时参考用)等档案名称,您只要按 Enter 键,则表示要使用其预设名称。
※ NUL 表示没有档案,所以最後组译的结果只产生 SMILE.OBJ 。
1.请输入 MASM SMILE
2.请按 Enter 键三次。
Microsoft (R) Macro Assembler Version 5.10
Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
Object filename [SMILE.OBJ]:
Source listing [NUL.LST]:
Cross-reference [NUL.CRF]:
50162 + 403867 Bytes symbol space free
0 Warning Errors
0 Severe Errors
※ ∶警告错误,表示组译器对某些事情不了解,不过,尚不致严重到使程式组译的结果发生错误。
※ ∶严重错误,会造成程式无法正常执行的错误
※ 在使用 LINK 之前,必须将错误修正。
※ OBJ 档案中包含的是组译後的二进位结果,它还无法被 DOS载入记忆体中加以执行,必须先加以链结(Linking)。
※ 以 LINK 将 OBJ 档案(SMILE.OBJ)链结成 EXE 档案(SMILE.EXE)时,不可使用附属档名 OBJ。
1.请输入 LINK SMILE
2.请按 Enter 键三次。
Microsoft (R) Overlay Linker Version 3.64
Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
Run File [SMILE.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
LINK : warning L4021: no stack segment
※ [.LIB]: ∶程式库,是一些程式的集合,在特殊的情况下,可将它与您的程式合并使用。
※ 由於 COM 档案并不使用堆叠段,所以错误讯息 "no stack
segment" 并不会影响程式的正常执行,只是增加我们的紧张罢了。
※ 使用 EXE2BIN 将 EXE 档案(SMILE.EXE),转换成 COM 档案(SMILE.COM)时,前者不必使用附属档名,而後者则必须指定附属档名,否则,会产生 BIN 档案(SMILE.BIN)。
※ 其实 BIN 档案与 COM 档案是完全相同的,但由於 COMMAND.COM只认识 COM、EXE 及 BAT 档案,所以 BIN 档案无法被正确执行。
1.请输入 EXE2BIN SMILE SMILE.COM
※ 现在,磁片上应该有 SMILE.COM 档案了,您只要在提示符号 A> 下,直接输入档案名称 SMILE ,就可以执行这个程式了。
1.请执行本程式。
※ 您是否觉得用组译器产生程式的方法,比 DEBUG 麻烦多了!
※ 以小程式而言,的确是如此;但对於较大的程式,您就可以发现其优点了。
※ 我们将 ASCII 程式以组译器方式再做一次,看看有无差异。
※ 首先,您必须先利用文书处理程式建立 ASCII.ASM 档案。
prognam segment ;start of segment
assume cs:prognam ;assume what's in CS
mov cx,100h ;put counter in CX
mov dl,0 ;first ASCII character
next: mov ah,2 ;Display Output Function
int 21h ;call DOS to print
inc dl ;next ASCII chacter
loop next ;do again, until counter = 0
int 20h ;return to DOS
prognam ends ;end of segment
end ;end of assembly
※ 在组合语言的原始程式码中,每一个程式列都包含四项元素∶
 start: mov dl,1 ;first character
 标记 运算子 运算元 注解
※ 在原始档案中加上注解可使程式更易了解,便於以後参考。
※ 每列注解栏以『;』开头。
※ 组译器会将『;』以後的叙述当做注解而不予理会。
※ 注解栏的资料不会出现在 OBJ、EXE 或 COM 档案中。
※ 在中文系统下,注解也可使用中文,但为了程式的可携性, 建议您还是练习英文吧!
※ 由於在撰写原始程式码时,我们并不知道每一程式列的位址 ,所以必须以符号名称来代表相对位址,称为『符号位址』。
如上 next: mov ah,2 ;Display Output Function之 next:
※ 我们通常在适当列上的标记栏位置上,键入符号位址。
※ 当要修改程式时,会发现使用标记让程式的弹性较大,因为 组译器会自动算出标记栏中的符号位址。
※ 标记(label)最长可达 31 个字元,因此我们在程式中,尽量以简洁易懂的文字做为标记。
※ 标记栏是每列的第一栏,可与所要标记的指令在同一列,也可位於该指令的前一列。
※ 现在,您可以将此 ASCII.ASM 档案组译成 ASCII.COM 了。
1.请输入 MASM ASCII 3.请输入 EXE2BIN ASCII ASCII.COM
2.请输入 LINK ASCII 4.请输入 ASCII
Microsoft (R) Macro Assembler Version 5.10
Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
Object filename [ASCII.OBJ]:
Source listing [NUL.LST]:
Cross-reference [NUL.CRF]:
48096 + 403883 Bytes symbol space free
0 Warning Errors
0 Severe Errors
※ 对初学者而言,MASM 的功能太强了,它的许多命令太复杂,以致常会妨碍了我们学习组合语言的兴致与效果。
※ 某些高明的指令事实上只对颇有经验的组合语言程式师有用,对我们而言,是太过高深了。
※ 事实上,使用 MASM 就好像在开 747 一样,只有 DEBUG 才有螺旋桨飞机的味道;因为,在 DEBUG 上面的控制全部都跟组合语言有直接的关系。
※ 如果您觉得组合语言蛮有趣的,终究您必须要用到 MASM。
※ 为了使学习组合语言程式有个好的开始,您必须要先排除那些华丽复杂的命令,将心力集中在最重要的几个命令上。
※ 随著您对 MASM 技巧的成长,最後您一定会感觉它是您在组合语言程式设计上的左右手。
※ 当您以组译器组合您自己设计的程式时,常会发生打字错误、 助忆符号名称拼错、十六进制数字少了h、逻辑错误等。
※ 组合语言的老手常给新鲜人的忠告是∶最好预期自己所写的 程式一定会有些错误。
※ 如果第一次执行程式後,就得到合理的答案,您最好还是带
著怀疑的心,因为它可能是错的。
※ 原则上,只要大体的逻辑架构正确,发掘程式中间题的过程 ,应该与撰写程式本身有相同的乐趣。
※ 撰写大程式时,最好能分成许多模组(Module),如此可使程式本身的目的较单纯,易於撰写与维修;另外也可让程式式中不同的部份之间的介面较清楚,并节省组译的时间。
※ 我们将撰写一个能从键盘取得一个十进位的数值,将其转换成十六进位数值而显示於萤幕上的『大程式』。
※ 要让 8088 微处理器执行这样的功能,我们必须先将此问题 分解成一连串的步骤,称为『程式规划』。
※ 首先,以流程图的方式,来确保整个程式的逻辑没有问题。
※ 您可使用一种界於英文与 8088 组合语言间的『虚拟语言』来解释程式,例如∶以 BASIC 的方式来表达程式规划的问题。
※ 最後就是将『虚拟程式码』改写成 8088 组合语言的程式。
※ 这种模组化的规划方式,称之为『由上而下的程式规划』。
※ 在真正撰写程式时,却是从最小的单位(模组)开始撰写,当每个模组都完成之後,再将其合并成大方程式;这种大处著眼,小处著手的方式称为『由下而上的程式设计』。
※ 我们的第一个模组是 BINIHEX,其主要用途是从 8088 的 BX 暂存器中取出二进位数,并以十六进位方式显示在萤幕上。
binihex segment ;start of segment
assume cs:binihex
mov ch,4 ;number of digits
rotate: mov cl,4 ;set count to 4 bits
rol bx,cl ;left digit to right
mov al,bl ;move to AL
and al,0fh ;mask off left digit
add al,30h ;convert hex to ASCII
cmp al,3ah ;is it > 9
jl printit ;jump if digit = 0 to 9
add al,7h ;digit is A to F
printit:mov dl,al ;put ASCII char in DL
mov ah,2 ;Display Output Finct
int 21h ;call DOS
dec ch ;done 4 digits ?
jnz rotate ;not yet
int 20h ;return from binihex
binihex ends ;end of segment
end

※ 利用旋转指令 ROL 旋转暂存器 BX 的内容,以便依序处理 4个十六进位数:
1.利用 CL 暂存器当计数器,记录暂存器移动的次数。
2.将 BX 的第一个十六进位值移到最右边。
※ 利用 AND 指令(两个运算元都为1时,其结果方为1)配合『遮罩,Mask』来遮掉不要的部份,以得到单纯的结果∶
1.先将 BL 值存入 AL 中,再以 0Fh (00001111)为遮罩 。
2.利用 AND 特性将 AL 的左边四个位元给遮掉。
※ 由於字元0~9之 ASCII 值为 30h~39h,而A~F之 ASCII值为 41h~46h,其中相差 7,所以得到的结果为∶若 AL 之内容小於 3Ah,则 AL 值只加 30h,否则 AL 值得再加 7h。
※ ADD 指令会将两个运算元相加,其结果存於左边的运算元内。

※ 旗标是只有一个位元的暂存器,集合成一个单独的十六位元 暂存器,称为『旗标暂存器,Flag Register』。
※ 某些组合语言指令(大部份是那些涉及比较、算术或逻辑运算的指令)执行时,会将相关旗标设定(1)或清除(0)。
※ 您经常会碰到的旗标是零值旗标(ZR/NZ)、正负号旗标(NG /PL)、溢位旗标(OV/NV)和进位旗标(CY/NC)。
※ 旗标保存了某个指令执行的结果,可用其他相关指令,查出 发出的情况,藉以因应而产生相对动作。
※ CMP 指令的动作很像减法,是将两个运算元的值加以比较。
※ 执行後,暂存器或记忆体的内容并未改变,只是相对的旗标 发生改变而已∶若 AL 的内含值小於 3Ah,则正负号旗标会设定成 NG(-),反之则为 PL(+)。
※ JL 指令可直接想成『假如小於就跳越,Jump if Less than』到运算元栏所指定的记忆位置。
※ CMP 指令和 JG 、JL 等条件式跳越指令一起使用,可以形成程式的『分支,branch』结构,是撰写组合语言程式常用的技巧。
※ 『JNZ,Jump if Not Zero』指令,其功能为当零值旗标未设定,则跳到运算元所指定的记忆位址。
※ 第二个模组 DECIBIN 用来接收键盘打入的十进位数,并将它转换成二进位数放於 BX 暂存器中,供 BINIHEX 程式使用。
※ 此程式之流程及程式码并不困难, 您是否已有腹案了?
decibin segment
assume cs:decibin
mov bx,0 ;clear BX for number
newchar:mov ah,1 ;keyboard input
int 21h ;call DOS
sub al,30h ;ASCII to binary
jl exit ;jump if < 0
cmp al,9d ;is it > 9d ?
jg exit ;yes, not dec digit
cbw ;byte in AL to word in AX
xchg ax,bx ;trade digit & number
mov cx,10d ;put 10 dec in CX
mul cx ;number times 10
xchg ax,bx ;trade number & digit
add bx,ax ;add digit to number
jmp newchar ;get next digit
exit: int 20 ;return from decibin
decibin ends ;end of decibin proc
end
※ 减法指令 SUB 会将左边的运算元减去右边的运算元,结果存於左边的运算元中。
※ JG 指令与 CMP 指令配合,若 CMP 指令左边运算元大於右边运算元,则跳至其本身运算元指定的记忆体位址。
※ CBW 指令会把 AL 暂存器中的位元组(8 位元 )转换成字组(16 位元 ),并放於 AX 暂存器中。
※ 实际上的结果是:
『若 AL 中的值为正,则 AH 填入 00h;反之,则 AH 填入 FFh』
※ XCHG 指令会把两个运算元的内含值互换。
※ 常用於需要暂时保留某个暂存器中的内含值时。
※ MUL 指令会将运算元的内含值与A暂存器的内容相乘,并将 结果存於A暂存器中。
※ 当然,我们还得加一个 CRLF 的小程式,用来列印归位字元 (CR)与换列字元(LF),使得结果显示的十六进位数不会盖掉原先输入的十进位数。
crlf segment
assume cs:crlf
mov dl,0dh ;carriage return
mov ah,2 ;display function
int 21h ;call DOS
mov dl,0ah ;linefeed
mov ah,2 ;display function
int 21h ;call DOS
int 20 ;return from crlf
crlf ends
end
※ 现在我们就可以将 BINIHEX 、 DECIBIN 及 CRLF 等模组合并成一个完整的大程式了。
※ 首先,我们必须将这三个模组程式改成『程序,Procedure』,
然後,再写一段简短的小程式来轮流呼叫每一个程序。
※ 所谓程序是由一群组合语言指令集合而成,通常用於执行特定的、定义完整的工作,在电脑语言中,常被称做『副程式』。
crlf proc near
mov dl,0dh
mov ah,2
int 21h
mov dl,0ah
mov ah,2
int 21h
ret
crlf endp
※ 类似 SEGMENT 与 ENDS 虚拟指令,PROC 与 ENDP 成对出现,用来识别并定义一个程序。
※ 其实,PROC 真正的作用只是告诉组译器∶所呼叫的程序是 属於近程(NEAR)或远程(FAR)。
※ 一般的程式是由 DEBUG 直接呼叫的,所以用 INT 20 返回,对於用 CALL 指令所呼叫的程序而言,则必须改用 RET 。
※ 返回指令 RET 会把控制权转移到堆叠顶端所指定的位址,而该位址是由呼叫此程序的 CALL 指令所放入的。
;************************************************
decihex segment ;DECIHEX -- Main Program
assume cs:decihex
repeat: call decibin ;keyboard to binary
call crlf ;print cr and lf
call binihex ;binary to screen
call crlf ;print cr and lf
jmp repeat ;do it again
;------------------------------------------------
decibin proc near ;PROCEDURE TO CONVERT DEC
............ ; ON KEYBD TO BINARY AND
decibin endp ; LEFT RESULT IN BX

;------------------------------------------------
binihex proc near ;PROCEDURE TO CONVERT BIN
............ ; NUMBER IN BX TO HEX ON
binihex endp ; CONSOLE SCREEN
;------------------------------------------------
crlf proc near ;PROCEDURE TO PRINT
............ ; CARRIAGE RETURN
endp ; AND LINEFEED
;------------------------------------------------
decihex ends
;************************************************
end

※ CALL 指令用来呼叫一个副程式(程序),并将控制权转移到 运算元内的副程式位址,同时将 CALL 的下一指令位址定为 『返回位址』,并存入堆叠中。
※ CALL 可分为近程(NEAR)及远程(FAR)两种∶
1.NEAR∶IP 暂存器的内容被放入堆叠中。用於程式与程序在同一记忆区段中。
2.FAR ∶CS 暂存器与 IP 暂存器的内容依顺序存入堆叠中。用於程式与程序在不同一记忆区段中。
※ 探讨了组合语言程式设计的较佳流程,希望能奠定您独立设计的基础,而如何利用模组化的观念来设计程式,则全仰赖您平时苦练、多看的苦功,只要再多参考相关的书籍,您必可从范例中获取的更多的应用技巧,祝您成功。
THE END
全文完 !!
                                                   gooor

2005-4-15

原创粉丝点击