注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

惊蛰

春雨惊春清谷天,夏满芒夏暑相连,秋处露秋寒霜降,冬雪雪冬小大寒

 
 
 

日志

 
 

汇编语言超浓缩教程(2)  

2007-01-08 20:11:17|  分类: 汇编语言 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

  客套话就不讲了。工欲善其事,必先利其器。与其说DEBUG 是编译器,倒不如说它是直译器DEBUGA命令只可将一行汇编指令转成机器语言,且立刻执行。真正编译器(MASM)的运作是利用文本编辑器(EDIT等)将汇编指令建成一个独立且附加名为.ASM的文本文件,称源程序。它是MASM 程序的输入部分。MASM将输入的ASM文件,编译成.OBJ文件,称为目标程序。OBJ文件仅包含有关程序各部份要载入何处及如何与其他程序合并的信息,无法直接载入内存执行。链结程序LINK则可将OBJ文件转换成可载入内存执行(EXEcute)的EXE文件。还可以用EXE2BIN,将符合条件的EXE文件转成COM文件(COM 文件不但占用的内存最少,而且运行速度最快)。
  下面我们用MASM写一个与用DEBUG写的第一个程序功能一样的程序。
  用EDIT编辑一个SMILE.ASM的源程序文件。
  源程序 DEBUG 程序
  prognam segment
  assume cs:prognam
  org 100h A100
  mov dl,1 mov dl,1
  mov ah,2 mov ah,2
  int 21h int 21
  int 20h int 20
  prognam ends
  end

  比较一下:1.因为MASM会将所有的数值假设为十进制,而DEBUG则只使用十六进制,所以在源程序中,我们必须在有关数字后加上代表进制的字母,如H代表十六进制,D代表十进制。若是以字母开头的十六进制数字,还必须在字母前加个0,以表示它是数,如0AH2.源程序增加五行叙述:prognam segment  prognam ends 是成对的,用来告诉 MASM LINK,此程序将放在一个称为PROGNAM(PROGram NAMe)的程序段内,其中段名(PROGNAM)可以任取,但其位置必须固定。assume cs:prognam 必须在程序的开头,用来告诉编译器此程序所在段的位置放在CS寄存器中。end用来告诉MASM,程序到此结束, ORG 100H作用相当于DEBUGA100,从偏移量100开始汇编。COM 文件的所有源程序都必须包含这五行,且必须依相同的次序及位置出现,这点东西记下就行,千篇一律。接着,我们用MASM编译SMILE.ASM
  输入 MASM SMILE ←不用打入附加名.ASM
  Microsoft (R) Macro Assembler Version 5.10
  Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
  Object filename [SMILE.OBJ]: ←是否改动输出OBJ文件名,如不改就ENTER
  Source listing [NUL.LST]: ← 是否需要列表文件(LST),不需要就ENTER
  Cross-reference [NUL.CRF]: ←是否需要对照文件(CRF),不需要则ENTER
  50162 + 403867 Bytes symbol space free
  0 Warning Errors ←警告错误,表示编译器对某些语句不理解,通常是输入错误。
  0 Severe Errors ←严重错误,会造成程序无法执行,通常是语法结构错误。

  如果没有一个错误存在,即可生成OBJ文件。OBJ中包含的是编译后的二进制结果,它还无法被 DOS载入内存中加以执行,必须加以链结(Linking)。以LINKOBJ文件(SMILE.OBJ)链结成 EXE 文件(SMILE.EXE)时,。
  1.输入 LINK SMILE ←不用附加名OBJ
  Microsoft (R) Overlay Linker Version 3.64
  Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
  Run File [SMILE.EXE]: ← 是否改动输出EXE文件名,如不改就ENTER
  List File [NUL.MAP]: ← 是否需要列表文件(MAP),不需要则ENTER
  Libraries [.LIB]: ←是否需要库文件,要就键入文件名,要则ENTER
  LINK : warning L4021: no stack segment← 由于COM文件不使用堆栈段,所以错误信息
  ←"no stack segment"并不影响程序正常执行

  至此已经生成EXE文件,我们还须使用EXE2BIN EXE文件(SMILE.EXE),转换成COM文件(SMILE.COM)。输入EXE2BIN SMILE产生 BIN 文件(SMILE.BIN)。其实 BIN 文件与 COM 文件是完全相同的,但由于DOS只认COMEXEBAT文件,所以BIN文件无法被正确执行,改名或直接输入 EXE2BIN SMILE SMILE.COM即可。现在,磁盘上应该有 SMILE.COM 文件了,你只要在提示符号C>下,直接输入文件名称 SMILE ,就可以执行这个程序了。

  你是否觉得用编译器产生程序的方法,比 DEBUG 麻烦多了!以小程序而言,的确是如此,但对于较大的程序,你就会发现其优点了。我们再将ASCII程序以编译器方式再做一次,看看有无差异。首先,用EDIT.COM建立 ASCII.ASM 文件。
  prognam segment ;定义段
  assume cs:prognam ;把上面定义段的段基址放入 CS
  mov cx,100h ; 装入循环次数
  mov dl,0 ; 装入第一个ASCII码,随后每次循环装入新码
  next: mov ah,2
   int 21h
   inc dl ;INC:递增指令,每次将数据寄存器 DL 内的数值加 1
  loop next ; 循环指令,执行一次,CX1,直到CX0,循环停止
  int 20h
   prognam ends ;段终止
  end ;汇编终止
  在汇编语言的源程序中,每一个程序行都包含三项元素:
    start: mov dl,1 ;装入第一个ASCII码,随后每次循环装入新码
    标识符 表达式 注解

  在原始文件中加上注解可使程序更易理解,便于以后参考。每行注解以与程序行分离。编译器对注解不予理会,注解的数据不会出现在OBJEXECOM文件中。由于我们在写源程序时,并不知道每一程序行的地址,所以必须以符号名称来代表相对地址,称为标识符。我们通常在适当行的适当位置上,键入标识符。标识符(label)最长可达31 字节,因此我们在程序中,尽量以简洁的文字做为标识符。现在,你可以将此ASCII.ASM 文件编译成 ASCII.COM 了。1.MASM ASCII2.LINK ASCII3.EXE2BIN ASCII ASCII.COM

  注意:当你以编译器汇编你设计的程序时,常会发生打字错误、标识符名称拼错、十六进制数少了h、逻辑错误等。汇编老手常给新人的忠告是:最好料到自己所写的程序一定会有些错误(别人告诉我的);如果第一次执行程序后,就得到期望的结果,你最好还是在检查一遍,因为它可能是错的。原则上,只要大体的逻辑架构正确,查找程序中错误的过程,与写程序本身相比甚至更有意思。写大程序时,最好能分成许多模块,如此可使程序本身的目的较单纯,易于撰写与查错,另外也可让程序中不同部份之间的界限较清楚,节省编译的时间。如果读程序有读不懂的地方最好用纸笔记下有关寄存器、内存等内容,在纸上慢慢比划,就豁然开朗了。   下面我们将写一个能从键盘取得一个十进制的数值,并将其转换成十六进制数值而显示于屏幕上的大程序。前言:要让8086执行这样的功能,我们必须先将此问题分解成一连串的步骤,称为程序规划。首先,以流程图的方式,来确保整个程序在逻辑上没有问题(不用说了吧!什么语言都要有此步骤)。这种模块化的规划方式,称之为由上而下的程序规划。而在真正写程序时,却是从最小的单位模块(子程序)开始,当每个模块都完成之后,再合并成大程序;这种大处著眼,小处著手的方式称为由下而上的程序设计

  我们的第一个模块是BINIHEX,其主要用途是从8086BX寄存器中取出二进制数,并以十六进制方式显示在屏幕上。注意:子程序如不能独立运行,实属正常。
   binihex segment
   assume cs:binihex
  mov ch,4 ;记录转换后的十六进制位数(四位)
  rotate: mov cl,4 ;利用CL当计数器,记录寄存器数位移动次数
  rol bx,cl ;循环寄存器BX的内容,以便依序处理4个十六进制数
  mov al,bl ;bx低八位bl内数据转移至al
  and al,0fh ;无用位清零
  add al,30h ;AL内数据加30H,并存入al
  cmp al,3ah ;3ah比较
  jl printit ;小于3ah则转移
  add al,7h ;AL内数据加30H,并存入al
  printit:mov dl,al ;ASCII码装入DL
  mov ah,2
   int 21h
   dec ch ;ch,减到零时,零标志置1
  jnz rotate ;JNZ当零标志未置1,则跳到指定地址。即:不等,则转移
  int 20h ;从子程序退回主程序
  binihex ends
   end

  利用循环左移指令ROL循环寄存器BX(BX内容将由第二个子程序提供)的内容,以便依序处理4个十六进制数:1. 利用CL当计数器,记录寄存器移位的次数。2.BX的第一个十六进制值移到最右边。利用 AND (逻辑运算:对应位都为1时,其结果为1,其余情况为零)把不要的部份清零,得到结果:先将BL值存入AL中,再利用AND0Fh00001111)将AL的左边四位清零。由于0到9的ASCII码为30h39h,而A到F之ASCII码为41h46h,间断了7h,所以得到结果:若AL之内容小于3Ah,则AL值只加30h,否则AL再加7hADD指令会将两个表达式相加,其结果存于左边表达式内。标志寄存器(Flag Register)是一个单独的十六位寄存器,有9个标志位,某些汇编指令(大部份是涉及比较、算术或逻辑运算的指令)执行时,会将相关标志位置1或清0, 常碰到的标志位有零标志(ZF)、符号标志(SF)、溢出标志(OF)和进位标志(CF)。 标志位保存了某个指令执行后对它的影响,可用其他相关指令,查出标志的状态,根据状态产生动作。CMP指令很像减法,是将两个表达式的值相减,但寄存器或内存的内容并未改变,只是相对的标志位发生改变而已:若 AL 值小于 3Ah,则正负号标志位会置0,反之则置1 JL指令可解释为:小于就转移到指定位置,大于、等于则向下执行。CMPJG JL等条件转移指令一起使用,可以形成程序的分支结构,是写汇编程序常用技巧。

  第二个模块DECIBIN 用来接收键盘打入的十进制数,并将它转换成二进制数放于BX 寄存器中,供模块1 BINIHEX使用。
  decibin segment
  assume cs:decibin
  mov bx,0 ;BX清零
  newchar:mov ah,1 ;
  int 21h ;读一个键盘输入符号入al,并显示
  sub al,30h ;al减去30H,结果存于al中,完成ASCII码转二进制
  jl exit ;小于零则转移
  cmp al,9d
   jg exit ;>右则转移
  cbw ;8al转换成16ax
  xchg ax,bx ;互换axbx内数据
  mov cx,10d ;十进制数10cx
  mul cx ;表达式的值与ax内容相乘,并将结果存于ax
  xchg ax,bx
   add bx,ax
   jmp newchar ;无条件转移
  exit: int 20 ;回主程序
  decibin ends
   end
  CBW 实际结果是:AL中的值为正,则AH填入00h;反之,则AH填入FFhXCHG常用于需要暂时保留某个寄存器中的内容时。
  当然,还得一个子程序(CRLF)使后显示的十六进制数不会盖掉先输入的十进制数。
  crlf segment
  assume cs:crlf
  mov dl,0dh ;回车的ASCII0DHDL
  mov ah,2
   int 21h
   mov dl,0ah ;换行的ASSII0AHAH
  mov ah,2
   int 21h
   int 20 ;回主程序
  crlf ends
  end

  现在我们就可以将BINIHEXDECIBINCRLF等模块合并成一个大程序了。首先,我们要将这三个模块子程序略加改动。然后,再写一段程序来调用每一个子程序。
  crlf proc near
  mov dl,0dh
  mov ah,2
  int 21h
  mov dl,0ah
  mov ah,2
  int 21h
  ret
  crlf endp

  类似SEGMENTENDS的伪指令,PROCENDP也是成对出现,用来识别并定义一个程序。其实,PROC 真正的作用只是告诉编译器:所调用的程序是属于近程(NEAR)或远程(FAR)。 一般的程序是由 DEBUG 直接调用的,所以用 INT 20 返回,用 CALL 指令所调用的程序则改用返回指令RET,RET会把控制权转移到顶所指的地址,而该地址是由调用此程序的 CALL指令所放入的。
  各模块都搞定了,然后我们把子程序组合起来就大功告成
  decihex segment ;主程序
  assume cs:decihex
  org 100h
  mov cx,4 ;循环次数入cx;由于子程序要用到cx,故子程序要将cx
  repeat: call decibin;调用十进制转二进制子程序
  call crlf ;调用添加回、换行符子程序
  call binihex ;调用二进制转十六进制并显示子程序
  call crlf
  loop repeat ;循环4次,可连续运算4
  mov ah,4ch ; 调用DOS21号中断4c号功能,退出程序,作用跟INT 20H
  int 21H ; 一样,但适用面更广,INT20H退不出时,试一下它
  decibin proc near push cx ;cx压入堆栈,;
  ┇ exit: pop cx ;cx还原retdecibin endp binihex proc near push cx
  ┇ pop cx retbinihex endp crlf proc near
   push cx
  ┇ pop cx retcrlf endpdecihex ends end

  CALL指令用来调用子程序,并将控制权转移到子程序地址,同时将CALL的下行指令地址定为返回地址,并压入堆栈中。CALL 可分为近程(NEAR)及远程(FAR)两种:1.NEARIP的内容被压入堆栈中,用于程序与程序在同一段中。2.FARCS IP寄存器的内容依次压入堆栈中,用于程序与程序在不同段中。PUSHPOP又是一对指令用于将寄存器内容压入、弹出,用来保护寄存器数据,子程序调用中运用较多。堆栈指针有个后进先出原则,像PUSH AXPUSH BX…POP BXPOP AX这样才能作到保护数据丝毫不差。

  汇编语言超浓缩教程到这要告一段落了,希望能奠定你独立设计的基础。而更多更好的技巧则全依赖你平时的积累了。祝你成功!

  评论这张
 
阅读(182)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017