论坛: 编程破解 标题: [20cn讲座]跟我学汇编[最后更新2005-9-16] 复制本贴地址    
作者: 286 [unique]    版主   登录
1 本讲座以汇编初学者或对汇编一点也不了解的读者为对象,汇编高手不属于该范围,但强烈建议高手指导并增补、修改本文。
2 任何读者可以跟此贴,提出疑问,或解答其中的问题,但对于所有跟贴,水贴、内容有错、毫不相干贴将直接删除,有意义的贴可能会合并到下一讲的内容中,合并后也将删除,请跟贴者谅解。同时按学习进步,请提问者逐步提,不要我没开口,你就问怎么编个病毒的问题。
3 借以抛砖引玉,但不希望大家只朝我扔砖头,希望大家踊跃思考,使之完善。



[此贴被 286(unique) 在 05月17日12时01分 编辑过]


[此贴被 286(unique) 在 09月16日17时46分 编辑过]

地主 发表时间: 04-12-24 16:54

回复: 286 [unique]   版主   登录
第负一讲 学习汇编前你应该知道的知识

  大家坐好了,。不要,不要,不要,男女同学不要相互,女同学不要对我....
 
1 汇编需要什么工具和程序,到哪里下载?
  目前阶段,汇编程序仅需要两个程序就够了。 masm.exe,link.exe。二者可由http://www.20cn.org/~unique/Download/Tool/masm.rar下载,前者是编译程序,后者是链接程序。
  另外,为了验证和调试程序,还需要一个程序debug.exe,该程序由windows本身就提供,所以就不提供下载地址了。
  将二者下载后,放到某一个目录中(任意目录都可以),考虑到很多命令需要通过键盘敲入,所以建议你不要把文件放入到长文件名目录、中文目录或很深的目录中。比如你可以建一个“D:\Masm”目录,并建议此后的程序都放这个目录,此后称这个目录为汇编目录。


2 学习汇编需要有哪些编程方面的知识。
  没有任何编程方面的知识,学习此语言等于缘木求鱼,所以请放弃学习的想法。一般来说至少要知道如下几点:
  *)程序的运行逻辑结构有顺序(按语句依次执行)、分支结构(IF...THEN...ELSE...),循环结构(FOR...NEXT)三种结构。
  *)知道什么是子程序,什么是调用。
  *)汇编程序员的视角。不同编程视角编程要求是不一样的。比如删除文件,
      >>用户的视角是找到“删除”按钮或菜单,然后单击一下即可。
      >>高级程序员的视角是知道删除的文件,并发出删除命令。这些通过API实现。
      >>汇编程员的视角是得到要删除的文件名,找到该文件所在位置,通过调用删除“中断命令”进行删除。
      >>操作系统开发人员的视角则是接到删除命令后,先找到系统根目录区,由根目录区的链接依次找到子目录区,直到找到要删除的文件,然后按照操作系统删除文件的规则对该文件名进行修改。比如DOS,只把第一个字符改成"?"。

  按程序语句等价的角度看,一行VB的打印语句,用汇编实现大约需要一百二十多行。知道汇编语言的视角后就要知道,前面的道路是坎坷的,没有耐心是不行的。想通过几分钟几行程序就完成很复杂的操作不是件容易的事。

3 学汇编有什么用?
  汇编产生于DOS时代或更早,而现在是Windows时代,所以可能遗憾地说:尽管还有批牛人在用汇编开发核心级程序,但我们几乎没什么用,除了必要时间能拿来分析一两个程序的部分代码之外,别的也就没干什么用了。并且并不是所有的汇编命令都能在windows下使用。而泛泛地追求“时髦”而学本语言,最后的结果是损了夫人又折兵。所以学之前你要考虑好。我劝那些为了当“黑客”而学汇编的人就此止步。


B1层 发表时间: 04-12-24 16:56

回复: 286 [unique]   版主   登录
第零讲 预备知识

1 一个汇编程序的编译过程是怎么样的。
  1)首先你需要找一个编辑器,编辑器用任何“纯文本”编辑器都可以。比如记事本。编好以后保存到汇编目录中。扩展名为asm,比如myfirst.asm。但这里建议你找一个能显示出当前行的编译器。这样出错后排错很容易。
  2)然后在DOS下进入D:\Masm目录中,输入“masm myfirst.asm",如果有错系统会提示出错的行位置和出错原因。
  3)然后再输入“link myfirst.obj”,即可看到当前目录下有一个myfirst.exe程序。

2 宏汇编和汇编有什么区别吗?
  二者的区别在于前者提供宏,后者不提供。后者已找不到了,所以你可以认为二者没有区别。

3 机器语言、汇编语言、高级语言的关系
  最早的计算机采用机器语言,这种语言直接用二进制数表示,通过直接输入二进制数,插拔电路板等实现,这种“编程”很容易出错,每个命令都是通过查命令表实现,既然是通过“查表”实现的,那当然也可以让计算机来代替人查表实现了。于是就产生了汇编语言,所以不管别人怎么定义机、汇语言,我就认为,二者是等价。后来人们发现,用汇编语言编某一功能的时候,连续一段代码都是相同或相似,于是就考虑用一句语言来代替这一段汇编语言,于是就产生了高级语言。因此,所有高级语言都能转化成汇编语言,而所以汇编语言又可转化成机器语言。反之,所有机器语言可以转成汇编语言(因为二者等价)。但并不是所以汇编语言都能转成高级语言。

4 计算机的组成
  通常都把计算机定义成五部分:运算器、控制器、存储器、输入系统、输出系统。
  为了简单其间,我们如此理解:运算器+控制器=CPU。存储器=内存(暂不包括外存,永不包括CACHE)。输入系统=键盘(不包括鼠标),输入系统=显示器(不包括打印机,绘图仪)。

5 寄存器和内存的区别
  寄存器在CPU中。内存在内存条中。前者的速度比后者快100倍左右。后面的程序要求每条指定要么没有内存数据,要么在有一个寄存器的参与下有一个内存数据。(也就是说,不存在只访问内存的指令)。

6 汇编语言的计数
  与生活中的计数不一样,汇编中的计数是从0开始的。比如16个计数,则是从0~15,而不是生活中的1~16。这一点看起来简单,真运算起来就不是件容易的事了,不信等着瞧。

7 进制问题
  又与生活中不一样的地方是进制。切记下面的常识:
  *)计算机内部存储都用二进制。
  *)我们的汇编源程序默认都用十进制。(除非你指明类型)
  *)我们用的调试程序debug默认的都是十六进制。(无法指明其他类型)
  其中十六进制的十六个个位数依次是:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F。

8 进制转换
  一个比较简单的方法是查表法。
  十进制 十六进制 二进制
      0      0      0000
      1      1      0001
      2      2      0010
      3      3      0011
      4      4      0100
      5      5      0101
      6      6      0110
      7      7      0111
      8      8      1000
      9      9      1001
      10    A      1010
      11    B      1011
      12    C      1100
      13    D      1101
      14    E      1110
      15    F      1111
 
  好了,结合6,7,8三条。大家来算一个“题”。某一组数据显示时,每个数据占了四个位置,
每行共十六个。问:十六进制的13位置在哪里(第几行,第几列)。
  格式如下:m m m m n n n n o o o o p p p p '注:之所以没用ABC是怕与上面十六进制弄混。
            r r r r s s s s t t t t u u u u


B2层 发表时间: 04-12-24 16:57

回复: 286 [unique]   版主   登录
第一讲 基础知识

1 访问内存
  程序在内存中,访问内存是几乎每一程序都要进行的操作,计算机对内存编址是线性的,也就是说是一维的,比如256M的内存,地址就应该是从0~(256M-1),这个地址称为物理地址或绝对地址。
1.1 地址表示
  但从汇编程序员的角度看,内存却是二维的,要说明一个地址,需要给出两个值,就象你在平面上指定一点需要说出(X,Y)坐标一样,汇编程序员的内存视角也需要两个“坐标”,前一个称为段地址(Segment),后一个称为偏移地址(Offset),该地址称为逻辑地址。
  比如“1234:3DF5”就是一个地址。“1F3F:”不是一个地址,因为他只有段地址,没有编移地址。注意此后的地址都用十六进制表示。
1.2 地址计算
  前面提到,计算机编址是一维的,汇编程序员是二维的,那么二者怎么换算呢?由后者到前者的换算方法是,“段地址串”后面加个“0”,然后再加上偏移地址。
  比如“1234:3DF5”(十六进制的加减运算参见相关资料)
12340 ‘串后加了一个0
3DF5
-----
16135  ’注意此串仍然是十六进制。
    所以,汇编程序员眼中的地址“1234:3DF5”就是物理地址(计算机编址):16135。
    知道了由后者向前者的转换,那么由前者向后者的转换呢?
  “不知道”,为什么不知道,继续往下看。
1.3 到底哪个地址对。
    知道了1.2的地址算法后,我又发现一个问题:
    “1000:6135”的物理地址是多少呢? 10000+6135=16135。
    “1001:6125”的物理地址呢? 10010+6125=16135。
    ......
    那么到底哪个对呢?问题的回答是这样的:假设我现在让你按一下“L”键,我可以告诉你如下几种方法中的一种或几种。1 请按一下“L”键; 2请按一下键盘上第四行第十个键;3 请按一下第十列中的第四个键;4 请按一下“K”右边的键;5 按标准指法单击一下右手无名指。
    举上面的例子也就是说,同一个地址有很多种表示方式,具体用哪一种,要看实际使用时的情况。但无论用哪种方式,只要能达到目的即可。(实际中该问题一般不会受此问题困扰,但初学时突然想不通)。
1.4 有多少内存可以访问
  无论是段地址还是偏移地址都是四位十六进制(如果不够四位,前面补0)。也就是说:总共可以访问的地址说是:0000:0000~FFFF:FFFF。 总共FFFF0+FFFF+1=10FFF0个地址。也就是不到1M的空间。
  记住如下结论:
  *)不管你实际内存有多少,目前我们只能访问不到1M的空间。
  *)而实际上连这1M也用不完。其中上端的384K的址只能读不能写,只能读,一般称为ROM。
  *)低端的640K可以读写。但这640K的低端100多K也不能随便写,因此DOS系统使用该区。
  *)原来1024M的内存,汇编程序只能使用其中400多K。这段内存的容易相当于一个普通文档的大小。不过这就足够了。

2 DEBUG的使用
先记住以下两个命令:D命令和Q命令。前者是显示内存内容,后者是退出DEBUG命令。
-------------以下为抄别的人内容---------------
DEBUG.EXE程序是专门为分析、研制和开发汇编语言程序而设计的一种调试工具,具有跟踪程序执行、观察中间运行结果、显示和修改寄存器或存储单元内容等多种功能。它能使程序设计人员或用户触及到机器内部,因此可以说它是80X86CPU的心灵窗口,也是我们学习汇编语言必须掌握的调试工具。

    1)DEBUG程序使用

在DOS提示符下键入命令:

    C>DEBUG [盘符:][路径][文件名.EXE][参数1][参数2]

  这时屏幕上出现DEBUG的提示符“-”,表示系统在DEBUG管理之下,此时可以用DEBUG进行程序调试。若所有选项省略,仅把DEBUG装入内存,可对当前内存中的内容进行调试,或者再用N和L命令,从指定盘上装入要调试的程序;若命令行中有文件名,则DOS把DEBUG程序调入内存后,再由DEBUG将指定的文件名装入内存。
2)DEBUG的常用命令
(1)退出命令 Q
    格式:Q
    功能:退出DEBUG,返回到操作系统。
(2)显示存储单元命令 D
格式1:D[起始地址]
    格式2:D[起始地址][结束地址|字节数]
    功能:格式1从起始地址开始按十六进制显示80H个单元的内容,每行16个单元,共8行,每行右边显示16个单元的ASCII码,不可显示的ASCII码则显示“・”。格式2显示指定范围内存储单元的内容,其他显示方式与格式1一样。如果缺省起始地址或地址范围,则从当前的地址开始按格式1显示。
例如:    -D 200          ;表示从DS:0200H开始显示128个单元内容
                -D 100 120    ;表示显示DS:0100-DS:0120单元的内容
    说明:在DEBUG中,地址表示方式有如下形式:
    段寄存器名:相对地址,如:DS:100
段基值:偏移地址(相对地址),如:23A0:1500

--------------------------小抄结束--------------------------------

3 验证第一节里的内容
运行“开始/程序/附件/MS-DOS命令提示符”(这是win2000,win98下自己找吧)
在“_”下输入D,显示
-d
1398:0100  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1398:0110  00 00 00 00 00 00 00 00-00 00 00 00 34 00 87 13  ............4...
1398:0120  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1398:0130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1398:0140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1398:0150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1398:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1398:0170  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
-
我们记下:1398:011C的值是个34。1389:011C的物理地址应该是:13A9C。
那么1000:3A9C的物理地址也应该是13A9C,他的内存也应该是34,(因为本来就是一个地址吗,就象第三行第十列和第十列第三行当然应该是同一个位置)。
-d 1000:3A9C
1000:3A90                                      34 00 87 13              4...
1000:3AA0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1000:3AB0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1000:3AC0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1000:3AD0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1000:3AE0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1000:3AF0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1000:3B00  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
1000:3B10  00 00 00 00 00 00 00 00-00 00 00 00              ............
-
果然如此,同样你可以验证:13A9:000C也肯定是指这一个地址,不信试试。


B3层 发表时间: 04-12-24 16:59

回复: 286 [unique]   版主   登录
4 DEBUG命令
-------------------继续小抄----------------
前面已学过:显示存储单元命令 D
再学一个命令
(1)修改存储单元命令 E

格式1:E[起始地址] [内容表]

格式2:E[地址]

功能:格式1按内容表的内容修改从起始地址开始的多个存储单元内容,即用内容表指定的内容来代替存储单元当前内容。

例如:―E DS:0100 'VAR' 12 34

表示从DS:0100 为起始单元的连续五个字节单元内容依次被修改为

'V'、'A'、'R'、12H、34H。

格式2是逐个修改指定地址单元的当前内容。

如:―E DS:0010

156F:0010 41.5F

其中156F:0010单元原来的值是41H,5FH为输入的修改值。若只修改一个单元的内容,这时按回车键即可;若还想继续修改下一个单元内容,此时应按空格键,就显示下一个单元的内容,需修改就键入新的内容,不修改再按空格跳过,如此重复直到修改完毕,按回车键返回DEBUG“-”提示符。如果在修改过程中,将空格键换成按“-”键,则表示可以修改前一个单元的内容。

-------------------小抄结束----------------

5 使用DOS时,汇编用户可以从DOS操作系统中得到什么?
  现在编程,通常很多功能都是通过调用系统API。很多高级语言都直接把这些API包装起来,以系统接口或函数的方式提供给用户,那么汇编函数都能得到什么呢?
  首先,汇编用户有很多东西可以调用。他们主要是:
  5.1 BIOS提供的接口。现在硬件与软件的区分已越来越不明显,很多硬件不仅仅是电路,而还要提供一些固化写入硬件的一部分“程序”,这些程序以ROM的方式出现,汇编用户最大的好处就是可以直接使用这些“程序”,这些使用不仅功能强大,而且效率非常高。
  5.2 DOS功能调用,作为操作系统也象BIOS一样向用户提供了相应的“程序”。这些程序在很大程序上扩充了BIOS。与BIOS不同的是,这部分程序放在内存中,它可以被修改。而BIOS中不能再修改。
  ==========================================================
  以上两种接口都通过一种相同的格式调用,这些程序统称为“中断”,现在先不要理解中断的本意,你现在可以认为是系统提供给你的函数。
  ============================================================
  5.3 系统共享数据区。编过程序的人都知道全局变量的好处,全局变量方便之外在于任何函数、过程都可以调用、读取、修改。全局变量不足之处是危险性,有一个过程改了这个变量值,其它的也得跟着改变了。DOS操作系统同样也提供了这样的共享数据区,该区是整个系统的共享区,任何程序都可以查找、修改。当然,修改某处必然会对其它程序造成影响。

6 再谈中断
  前面5.2已提到中断了,现在问题是不同硬件不一样,即使相同硬件的ROM,不同版本,各个BIOS中断程序所处的位置也不一样,DOS中断也一样,不同版本、不同配置,在内存位置也不一样。那么你使用某一个中断,系统怎么知道你使用的那个中断程序在哪呢?
  为了解决这一问题,DOS会在启动的时候,把所有这些(BIOS和DOS)中断的首地址保存到一个地址。这个地址很容易记,这段地址是内存的绝对零地址(0000:0000)。前面已讲过,每个地址在汇编程序员角度来看是二维的,也就是分为段地址和偏移地址。每个地址各占两个字节,所以要表示这个二维地址需要4个字节。所以每个中断首地址由4个字节表示。一共256个中断,占用了1024个字节的位置。
  另外需要注意的是,这4个表示地址的字节,数据是由低向高的。比如12 34 56 78所表示的地址是:7856:3412。
一般用INT M表示中断M,如果M是十六进制,则在后面加上一个H。比如19号中断,十六进制应该是13H。所以该中断就是INT 13H。

7 再谈系统共享数据区
  该共享数据区在绝对地址:0040:0000开始。

8 验证我上面说的内容
  8.1 找中断
  运行DEBUG后。输入D 0000:0000。显示绝对零地址的内容。
  C:\>debug
-d 0:0
0000:0000  68 10 A7 00 8B 01 70 00-16 00 9B 03 8B 01 70 00  h.....p.......p.
0000:0010  8B 01 70 00 B9 06 0E 02-40 07 0E 02 FF 03 0E 02  ..p.....@.......
0000:0020  46 07 0E 02 0A 04 0E 02-3A 00 9B 03 54 00 9B 03  F.......:...T...
0000:0030  6E 00 9B 03 88 00 9B 03-A2 00 9B 03 FF 03 0E 02  n...............
0000:0040  A9 08 0E 02 99 09 0E 02-9F 09 0E 02 5D 04 0E 02  ............]...
0000:0050  A5 09 0E 02 0D 02 DC 02-B8 09 0E 02 8B 05 0E 02  ................
0000:0060  02 0C 0E 02 08 0C 0E 02-13 0C 0E 02 AD 06 0E 02  ................
0000:0070  AD 06 0E 02 A4 F0 00 F0-37 05 0E 02 71 84 00 C0  ........7...q...
-u 0070:018B
0070:018B 1E            PUSH    DS
0070:018C 50            PUSH    AX
0070:018D B84000        MOV    AX,0040
0070:0190 8ED8          MOV    DS,AX
0070:0192 F70614030024  TEST    WORD PTR [0314],2400
0070:0198 754F          JNZ    01E9
0070:019A 55            PUSH    BP
0070:019B 8BEC          MOV    BP,SP
0070:019D 8B460A        MOV    AX,[BP+0A]
0070:01A0 5D            POP    BP
0070:01A1 A90001        TEST    AX,0100
0070:01A4 7543          JNZ    01E9
0070:01A6 A90002        TEST    AX,0200
0070:01A9 7422          JZ      01CD
首先,D命令把中断首地址显示出来。每4个表示一个地址。其中INT 0的中断首地址为:00A7:1068,INT 1的中断地址为:0070:018B.......0070:018B是中断3的首地址。后面那个U命令就表示显示该地址的“中断程序”的内存。
    你们可以试着找找INT 13H的位置在哪。
  8.2 验证系统共享数据区
    系统共享数据区内容极为丰富,我实在记不住哪么多了。我曾记在一个本上,可惜那个本早在N年前(3<N<6)就丢了。兄弟们谁找到这个地址的内容,一定要贴上来,这里有东西可以让大家眼界大开。
    前几年,我用的286计算机是黑白显示器(555555~~~~~~~~~,别嫌我老、旧、慢呀),可当时有个游戏非要彩显,不是彩显不让运行。我就是改了这个区的某一个位,让哪游戏“以为”我用的是彩显,于是游戏能用了。虽然不好看,但总能用。
    在DOS下,你每按一个键,系统都会记下来,下面我们一起找找这个键盘缓冲区的地址。知道这个地址,你就可以作一个“虚拟”键盘,通过发命令来模拟某个人在按键。这个地址位于:0040:001E。 其中每个键有两个字节,一个字节是ASCII码,一个是扫描码。共16个。
C:\>debug
-d 40:0
0040:0000  F8 03 F8 02 E8 03 E8 02-BC 03 78 03 78 02 80 9F  ..........x.x...
0040:0010  22 C8 00 80 02 28 00 00-00 00 2A 00 2A 00 20 39  "....(....*.*. 9
0040:0020  34 05 30 0B 3A 27 30 0B-0D 1C 64 20 20 39 34 05  4.0.:'0...d  94.
0040:0030  30 0B 3A 27 30 0B 0D 1C-71 10 0D 1C 64 20 00 00  0.:'0...q...d ..
0040:0040  A2 00 C3 00 A2 AF 09 E1-C8 03 50 00 00 10 00 00  ..........P.....
0040:0050  00 18 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0040:0060  0F 0C 00 D4 03 29 30 7F-03 00 C0 00 A1 B7 11 00  .....)0.........
0040:0070  00 00 00 00 00 00 00 00-14 14 14 00 01 01 01 01  ................
-d 0040:0000
0040:0000  F8 03 F8 02 E8 03 E8 02-BC 03 78 03 78 02 80 9F  ..........x.x...
0040:0010  22 C8 00 80 02 28 00 00-00 00 2A 00 2A 00 3A 27  "....(....*.*.:'
0040:0020  30 0B 30 0B 30 0B 30 0B-0D 1C 64 20 20 39 30 0B  0.0.0.0...d  90.
0040:0030  30 0B 30 0B 30 0B 08 0E-08 0E 34 05 30 0B 00 00  0.0.0.....4.0...
0040:0040  1F 00 C3 00 A2 AF 09 E1-C8 03 50 00 00 10 00 00  ..........P.....
0040:0050  00 18 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0040:0060  0F 0C 00 D4 03 29 30 7F-03 00 C0 00 24 B8 11 00  .....)0.....$...
0040:0070  00 00 00 00 00 00 00 00-14 14 14 00 01 01 01 01  ................
-
既然是键盘缓冲区,每个输入的键都会显示在该区中,第一次我只输入了“d 40:0”,所以你可以在此后显示数据右边字符中找到这些字符,注意是间隔开的。
第二次我输入“d 0040:0000”,则右边显示的是“d 0040:0000”的内容。你可以找找。

B4层 发表时间: 04-12-24 17:00

回复: 286 [unique]   版主   登录
第二讲 内存映象

  之所以把这个内存单独放一章,是为了说明它的重要性,后面的几乎很多程序都需要你对这一章的理解。这里的内存映象就是指当你把一个可执行文件(EXE或COM文件)放到内存后,整个内存“看”起来是什么样子的。
  前面讲过,这里汇编程序只能访问1M的内存空间,所以下面就以1M内存为例。并且以DOS操作系统作为讲解对象,所以所编出来的程序也仅是DOS程序。事实上,通过winasm可以访问远远超过1M的空间,并且可以编出FOR windows的程序。但那是另外的话题。我们暂且不说那些。

2.1 内存映象
  首先,这1M内存如果我们不再以二维的方式看,而是一维的,线性地看(二维和一维的转化方式参见前面章节)。但描述还是以二维的方式描述,从最底端到最高端依次是:
  1 中断向量区:该区由0000:0000~0000:03FF。这里存着系统的所有中断的中断向量表,对于中断向量表,你现在先理解为一些程序的首地址。由这个地址你就能找到该程序。
  2 系统数据区:该区由0040:0000~0040:XXXX(不好意思,忘了),这里存着整个系统中,DOS操作系统要用的数据,由于这个区的数据对用户是开放的,所以用户当然也可以从这里读出来用。
  3 DOS操作系统区:操作系统常驻内存,你向计算机发的每个命令其实都是操作系统执行的。这个区的大小主要是由操作系统的版本和用户的配置大小决定,如果是驱动程序配置,就放到根目录下的config.sys里,如果是程序,就放到autoexec.bat里。这里设置在现在的windows 95/98/nt/me/2000/xp/2003中仍然有,所以我就不多说了。
  4 用户程序,这个当然就是你执行的程序了,这种程序分两种,一种是扩展名为com文件,一种是exe文件,从程序内部看,前者程序的四个段重合(后面要讲这四个段),所以最大长度只等于一个段,用前面段地址的理解就是com文件最大只能是64K,所以com文件只适合小的程序。而exe,四个段可任何分配,并可扩充段,而且每个段的段地址可以任何改动,因此exe的访问内存能力大多了。这种格式访问能力只受地址结构的限制了。
    用户程序所占的内存大小完全由程序本身决定,但最大,只能到640K。这一点,怪不得别人,只能怪当前计算机软硬件设置高手高手高高手们(包括比尔盖茨)们的失误了,60年代的超级计算机只有36K的内存,所以他们就在80年代得到一个结论:640K的内存足够了。
    如果用户程序大于由操作系统所占内存的顶底到640K之间的内存量,就会显示:内存不够,因而程序不能执行。这种现象对于一开始就用windows的人来说,几乎没见过,但对于一开始用DOS并打汉字的人来说,再正常不过。如果小于这段内存,多余部分就空着。
  5 从640K到1M-64K,这段内存就很难说清了。这段内存中有一部分被硬件占有,有一部分是显示缓冲区点有,还有一部分是系统ROM占有。
  6 从1M-64K到1M之间的这段64K的内存叫作HMA。这段内存是小孩没娘,说来话长,我们先不说他。
   
 

B5层 发表时间: 04-12-24 17:36

回复: 286 [unique]   版主   登录
2.2 验证上面的理论

2.2.1 中断向量表
    中断向量表就是所有中断向量首地址表,这里保存着每个中断程序的首地址,几乎所有的汇编书都把中断后面后面的章节中,并且对中断的解释也仅从字面意思解释,所以导致大学对中断的不重要和误解。没耐心的没到这个章节就不学汇编了,有耐心的到这里才豁然开朗。我现在不讲中断的原意。我直接告诉你,你把中断当成API也许更合适。也就是说,别人把很多已作好的功能放到了内存中。并且把调用这一功能的号告诉了你,你只要调用这些功能号,系统就自动从这个中断向量表中找到对应的中断,然后执行你的功能。
    首先让你感受一下中断的魅力一下吧。比如中断21H的2A功能调用是读取系统的日期,这个调用的规则是,调用前AH寄存器置为2A。调用后年在CX中,月在DH中,DL在日中,星期在AL中。
-a
139D:0100 mov ah,2a
139D:0102 int 21
139D:0104 int 3
139D:0105
-g=100

AX=2A05  BX=0000  CX=07D4  DX=0C18  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=139D  ES=139D  SS=139D  CS=139D  IP=0104  NV UP EI PL NZ NA PO NC
139D:0104 CC            INT    3
-
可能上面的程序你目前还看不懂。不过没关系,“mov ah,2a”表示调用功能号是2a号。“int 21”表示调用十六进制21号中断,“int 3”表示3号中断,表示程序运行到这一句时停一下。“g=100”表示从“139D:0100 ”开始执行。
AX=2A05  BX=0000  CX=07D4  DX=0C18  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=139D  ES=139D  SS=139D  CS=139D  IP=0104  NV UP EI PL NZ NA PO NC
表示执行的结果。其中CX是年,这个年是由CX中存。07D4十进制就是2004年。DH+DL=DX,所以DH=0C,DL=18。二者转化为十进制就是DH=12,DL=24,也就是今天了。AX=AH+AL=2A05,所以AL=05。那就是今天是星期五。
上面可能你们现在还看不懂,不过通过解说你应该可以知道,仅仅两行命令,就读到了现在的值。现在需要作的就是把这些值提取出来用作他用了。

    从中断的作来与中断向量表又有什么关系呢?原来你在汇编里运行int 21时,系统就在上面的中断向量表中找到int 21的中断地址,该中断的地址应该位于:0000:0084~0000:0087,具体算法前面已说明了。
-d 0000:0084 0087
0000:0080              7C 10 A7 00                              |...
-
找到内容是:00A7:107C。然后系统就转到这个地址执行int 21。

2.2.2 系统数据区前面都已说明过。不再多说。系统区,很多DOS中断程序实现部分就在这个区。程序运行区依不同的程序而不用。

2.2.3 640K~1M之间,这期间有些地方是ROM,有些地方是硬件的BIOS区。我仅以两个例子说明这一区。
  ROM区:ROM区就是只读内存,也就是说这个区的数据只能读不能写。比如F000:0000开始的内存是ROM。我们来写一下,然后再看看效果。
-d f000:0000 0005 '显示由F000:0000到F000:0005的六个字节值。
F000:0000  04 E8 A2 FF F9 C3                                ......
-e f000:0000    '修改命令
F000:0000  04.00  E8.00  A2.00  FF.00  F9.00  C3.00'注意,.后面的是我改的,把这几个值都改成0了。
-d f000:0000 0005 '再次显示这个区的数据。
F000:0000  04 E8 A2 FF F9 C3                                ......
-
通过上面测试,发现该区数据仍然未改变。但你要是试别的RAM区的,肯定会变。如果想试你自己试试吧。

  显示缓冲区:在文本方式下,B800:0000开始的地址保存着屏幕上每个字符位置的值。在文本方式下,屏幕被分为80 X 25。每个位置有两个值,一个值是ASCII字符,一个值是该ASCII的属性值(主要是颜色)。所以一个屏幕共有80X25X2=400个字符。
我们来改:
-d b800:0000 0010 '显示屏幕缓冲区的内容,注意此时本行最左边的“-”是屏幕左上角。
B800:0000  2D 07 64 07 20 07 62 07-38 07 30 07 30 07 3A 07  -.d. .b.8.0.0.:.
B800:0010  30                                                0
-
看上面的命令,屏幕最上边一行是“-d b800:0000 0010”,所以他的内容就是“2D 07 64 07 20 07 62 07-38 07 30 07 30 07 3A 07”其中,2D是“-”的ASCII值,07是“-”的属性值。64是“d”的ASCII值,07是“d”的属性值。。。。。
现在修改这些值。我把左上角的字改成黄颜色的“-”,那当然是改b800:0001的属性值了。
-e b800:0001  0e
是不是左上角的颜色变成黄色了吗?
好了,把第二个字符变成绿色的“-”吧?
-e b800:0002 2d 0b
变了吗?





[此贴被 286(unique) 在 12月28日13时23分 编辑过]

B6层 发表时间: 04-12-24 19:45

回复: xiean [xiean]   论坛用户   登录


特来捧场,最近的帖,就此帖最有价值

B7层 发表时间: 04-12-27 10:18

回复: NetDemon [netdemon]   ADMIN   登录
特来骂人,最近的人,就B8最该骂

B8层 发表时间: 04-12-27 11:10

回复: lida1818 [lida1818]   论坛用户   登录
请教286:
===========================
表示执行的结果。其中DX是年,这个年是由CX中存。07D4十进制就是2004年。DH+DL=DX,所以DH=0C,DL=18。二者转化为十进制就是DH=12,DL=24,也就是今天了。
===========================
这个后面的我都知道,但是DX是什么值?我一直没搞清楚。“其中DX是年”怎么讲?还望详解。谢谢。

这一段用C表达应如下:
#include <stdio.h>
#include <dos.h>
int main()
{
    union REGS regs;

    regs.h.ah=0x2a;
    int86(0x21, &regs, &regs);

    printf("%d年 %d月 %d日 星期 %d",regs.x.cx,regs.h.dh,regs.h.dl,regs.h.al);
}



B9层 发表时间: 04-12-27 16:27

回复: -t [bug_me]   论坛用户   登录
中断21H的2A功能调用:

功能描述: 取系统日期
入口参数: AH=2AH
出口参数: CX=年(1980~2099),DH=月(1~12),DL=日(1~31)
          AL=星期几(0=Sunday,1=Monday,...)



B10层 发表时间: 04-12-28 00:35

回复: cszxx [cszxx]   论坛用户   登录
怎样用汇编语言实现整数集合的输入和输出啊(用一个定长的数组存储就可以了)
我是初学的,编不出来,在网上也搜不到,估计太简单了吧
大哥有没有时间帮我搞搞啊
或者介绍下有关的书或网页也可以啊
拜托啦~


B11层 发表时间: 04-12-30 22:28

回复: haoweir [haoweir]   论坛用户   登录
可以添加win32汇编不?

B12层 发表时间: 05-01-09 00:37

回复: woodhead [woodhead]   论坛用户   登录
引文:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
此时你借助SOFTICE动态调试能,很快找到序列号,在这我们今天用暴力法破解,注意 :

:0040158D FF1500204000            Call dword ptr [00402000]//真假序列号比较核心(调用函数lsrcmpa比较)
:00401593 85C0                    test eax, eax//用eax当旗帜,如相等,则eax=0
:00401595 7516                    jne 004015AD//如不跳转则注册成功

看明白了吗?要让程序接受任何注册码就只要把JNE (=不相等就跳)改成JE (=相等就跳).或把改成空指令nop(什么也不执行),这样前一各改法要注册就只能输入错误的注册码,后者可任意注册码。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
上面的代码我看不懂。ax为什么和自己比较会有不相等的时候呢?程序的源代码的确和书上一样。我想了很久也没搞明白,谁能说一说原因。在这里先谢你了。


==========================================================================
谢谢了。我把汇编和高级语言搞混了。一看test我就想起高级语言里的比较大小来了,真没脑子。看样子我也够粗心的,没好好查查书就来问了。
[此贴被 woodhead(woodhead) 在 01月10日00时10分 编辑过]


[此贴被 woodhead(woodhead) 在 01月11日00时47分 编辑过]

B13层 发表时间: 05-01-09 18:41

回复: zuosi [zuosi]   论坛用户   登录
前面一定还有一条SBB 或着 XOR 的指令 ,来设置eax当旗帜.TEST 只进行AND 来设置标志位.本身不改变.如果EAX 为0000 0000H. TEST EAX,EAX 以后ZF一定为1,

B14层 发表时间: 05-01-10 22:54

回复: ghost518 [ghost518]   论坛用户   登录
286:前面的都看完了,继续讲啊~~你好长时间没上喽~

B15层 发表时间: 05-01-19 18:44

回复: hangj [hangj]   论坛用户   登录
很快汇编就进入64位,早点上手学吧!
我提供一个软件本身是一个Win32程序(PE32格式)在下载的包里有64位的PE和ELF文件各一个,32位ELF文件一个,供大家研究!

图形界面。支持实模式、保护模式、64位模式三种模式指令集的反汇编. 支持DOS系统COM和EXE可执行文件格式,支持32位和64位PE文件格式(windows可执行文件),支持32位和64位ELF文件格式(Linux可执行文件),共计六种文件格式。支持部分指令的虚拟执行调试。

下载页面:
http://pay500.com/s/s56504.htm



[此贴被 hangj(hangj) 在 01月22日11时43分 编辑过]

B16层 发表时间: 05-01-22 11:40

回复: hangj [hangj]   论坛用户   登录
此间代码被删除

[此贴被 286(unique) 在 02月22日10时51分 编辑过]

B17层 发表时间: 05-01-25 11:11

回复: bluezzb [bluezzb]   论坛用户   登录
286老师什么时候有空啊?再讲下去啊...
期待!

B18层 发表时间: 05-02-17 16:51

回复: 286 [unique]   版主   登录
可执行文件内存映象
DOS下可执行文件有两种(BAT是批处理文件,他只是简单调用DOS内部命令或其它程序,所以此处不认为它是可执行文件),一种是COM文件,一种是EXE文件,前面提到,COM文件一般小于64K。EXE文件则可以任意大。为什么呢?
说到这里,还要提到段。每个段64K。段的作用就是数据组织单位。段的类型有三种:代码段(Code Segment,简称CS)、数据段(Data Segment,简称DS)、栈段(Stack Segment,简称SS),另外还有一个附加数据段(Extra Segment,简称ES),它的用与数据段DS可以认为完全一样,当数据段的64K不够用,或你就需要把数据放到两个段中以便移动、复制、比较时,才用到附加数据段ES。(当然,移动、复制、比较操作在一个段中也可以完成)。
1 段的作用。
1.1 代码段(CS),程序装入内存中,DOS怎么知道是从哪里执行呢?答案就是系统自动从代码段指定位置开始执行,并且始终在代码段中执行。内此代码段CS的作用就是保存所有的指令。这里所说的代码也就是汇编指令了。所以编写汇编程序也就主要是编写代码段中的代码。
1.2 数据段(DS)、附加段(ES),顾名思义,数据段中存的就是数据,这些数据供代码段的程序调用。附加段就是附加数据段。作用与数据段相同。
1.3 栈段(SS),这个段非常重要,但实际上,你在使用中,似乎用不着这个段,但实际上,这是黑客编程中最重要的一部分,而且系统会不停地“偷偷地”使用这个段,正是这个偷偷地用,使得系统的很多动作被记录到这个段中。还有两点,你必须记住:一是如果你使用了这个栈,比如你把数据存到这个栈中,则必须有相应的出栈命令,并且入几个数据,就得出几个数据,多一个或少一个,你的程序就可能导致死机或异常;二是你要把握操作时机,比如你不能在系统使用栈的前后使用栈,比如你在调用子程序之前入栈,而在子程序中出栈,而在系统调用子程序时,系统也要使用栈,这种也将导致出错。
栈就是一种先入后出(也有称为后入先出)的结构,有地址由小到大的增加栈,有地址由大到小的逆向减栈。
2 段重叠
从上面,我们可以看到,CS,DS,SS三者作用各不相同,内存就是象录音磁带,录新歌,则旧歌被删,带子上存的始终是最后录的那段音乐。因此,如果重叠则必然相互冲突。那还能重叠吗?
这里所说的重叠不是指内容重叠,而是指概念上的重叠,即数据相互放到一个段中,但相互可以区分开。比如某一段既有数据也有代码,则代码在每要执行到数据之前加一个跳转指令跳过这段代码。这个跳转指令要求用户在编程的时候加上。
而栈段呢?栈段有自己的特殊性,特殊就在于系统也会自动地使用,而用户则又在不知道系统在使用的情况下使用。避免这种冲突的方法就是采用逆向的栈段。
2 COM文件内存映象。
  COM文件被读到内存中后,该文件的前100H个字节被操作系统使用,操作系统使用这256个字节保存一些系统要使用的数据,汇编语言编程者不能在这里存自己的数据,但在知道这此数据的作用后可以使用其中的数据。从100H开始,就是程序的开始了。COM文件之所以最大只能有64K,其原因是COM文件的四个段是相互重叠的。也就是说,CS,DS,SS,ES四个段的地址都指向这个COM文件的100H处。程序代码、数据、栈都在由100H到64K的区域内。如何把三者分开呢?栈段采用逆向栈,这个栈由64K开始,随着数据入栈,则地址就减小。这样作的好处是,栈段由高端向低端进展,可以详细与数据、代码分开;坏处也不言而喻,假如一个COM程序大量用到栈(比如是个递归程序)因此栈就不停地降低,而程序代码本身也很多,甚至不停地申请新空间,这样数据和栈就会在中间碰头,导致程序被破坏。
  区分开数据代码段与栈段后,下面讨论把数据段和代码段也分开。这个简单的多,只要逻辑上分开就可以。不过一般的方法就是:在100H处放一个跳转指令,随后放数据,然后再放置其它的代码。而100H处的跳转指令就跳到这里。
因此,COM文件内存映象就是:
CS:0000  (由于COM的CS,DS,SS,ES三段重叠,因此此行前CS,写成DS,SS,ES都一样)。 
CS:0100    一个跳转到YYYY地址的跳转指令。
CS:0101    本程序所需要用到的数据
CS:XXXX    数据结束处。
CS:YYYY    程序代码保存处。
CS:ZZZZ    程序代码结束处。
CS:FFFF    栈段开始处(注意栈是地址越来越小,所以这里是开始而不是结束处),也是程序的结束处。另外,此处FFFF与前面XXXX,YYYY,ZZZZ不一样,这里是十六进制的64K。

3. EXE文件
  比起COM文件,EXE文件要复杂一些,他的复杂就在于COM文件前面规定了100H个字节用于系统使用,而EXE文件则有个文件头,文件头的大小看具体内容多少。文件头的内容使得EXE看起来复杂了,但也更灵活了。更重要的是,对于病毒设计者,这个文件头使他们如鱼得水。因为文件头处
EXE文件的内存映象为:
XXXX:0000 文件头
XXXX:YYYY 文件头结束处。

CS:0000 代码段开始处
CS:ZZZZ 代码段结束处

DS:0000 数据码段开始处
DS:WWWW 数据码段结束处

SS:0000 栈段开始处
SS:UUUU 栈段结束处

ES:0000 附加段开始处
ES:VVVV 附加段结束处

说明:
1 上述ES可以没有,要看实际需要
2 CS,DS,ES,SS的顺序也是看编程者是怎么安排的,好在用户不必关心他的具体位置。
3 由上可见,CS,DS,ES,SS的段地址肯定保存到了文件头中。
4 由上可见,实际执行的只是CS,因此DS,ES,SS的首地址,CS肯定要想办法知道。:)

B19层 发表时间: 05-02-22 10:53

回复: mingren123 [mingren123]   论坛用户   登录
64位汇编有什么要改的吗??我门现在学的都是16位的汇编啊

B20层 发表时间: 05-03-09 11:16

回复: liyi1112 [liyi1112]   论坛用户   登录

等等
别请这么快吗
我先收藏在说
呵呵


B21层 发表时间: 05-03-10 17:17

回复: ziaichen [ziaichen]   论坛用户   登录
我们这学期开了汇编,真的不是很好学啊
多谢286的这个讲座!

B22层 发表时间: 05-03-13 10:34

回复: laosan [laosan]   论坛用户   登录
头有点晕啊,但还是谢谢了


B23层 发表时间: 05-03-15 13:13

回复: naforce [naforce]   论坛用户   登录
能不能讲一下中断?


B24层 发表时间: 05-03-16 15:49

回复: Neptune [admin_20cn]   论坛用户   登录
我要看32位的汇编

B25层 发表时间: 05-04-07 19:41

回复: kevin789 [kevin789]   论坛用户   登录
什么是汇编?
坛中央灌水办


B26层 发表时间: 05-04-17 17:36

回复: netime [netime]   论坛用户   登录
偶是菜鸟
看了老长时间才看出来点道道的~
楼主写的东东对俺真的好有用诶
支持支持~~

B27层 发表时间: 05-04-19 14:08

回复: ziaichen [ziaichen]   论坛用户   登录
斑竹:好象很久没更新了啊
正在期待着你的下一讲呢

B28层 发表时间: 05-04-19 18:45

回复: X-H [xh21317]   论坛用户   登录
讲的好!

B29层 发表时间: 05-04-21 12:26

回复: cnhp [cnhp]   论坛用户   登录
会出第二期汇编教程吗?我在关注

B30层 发表时间: 05-04-22 11:00

回复: yjyygywcw [yjyygywcw]   论坛用户   登录
我们正在学汇编了。能亲自指点吧:QQ:: 123692404


B31层 发表时间: 05-04-29 11:19

回复: NullNet [hackerjune]   论坛用户   登录
286
我转载了下哦。


B32层 发表时间: 05-05-02 16:24

回复: shale2002 [shale2002]   论坛用户   登录
偶正在学习汇编,看了这个帖子很有帮助

B33层 发表时间: 05-05-16 20:47

回复: 286 [unique]   版主   登录
前一段时间实在太忙了,一直没更新。好了,现在继续。也找不到前面说到哪了,好在下面内容是独立的,所以列为第三章。
-------------------------------------------------------------


第三章 汇编指令
3.1 什么是机器语言
前面提到“最早的计算机采用机器语言,这种语言直接用二进制数表示,通过直接输入二进制数,插拔电路板等实现,这种“编程”很容易出错,每个命令都是通过查命令表实现”。
比如要执行21号中断,需要查表,得到21号中断的指令就是CD 21。这样不管你通过什么方式,在内存指令位置,写入两个字节,一个是CD(这可不是音乐光盘,而是二进制数,转成十进制就是205),另一个是21(同样是十六进制,十进制是33)。
上面就是机器语言。

3.2 什么是汇编语言
前面也提到“既然是通过“查表”实现的,那当然也可以让计算机来代替人查表实现了。于是就产生了汇编语言”,汇编语言产生的重要目的就是用容易记的符号来代替容易出错的二进制数(或十六进制数)。
比如前面的21号中断,机器语言是CD 21。而汇编语言就规定中断用int表示(interrupt的前三个字母),21号中断就成了int 21h。其中21后面的h表示是表示这个21是十六进制。由于大小写不敏感,所以int 21h写成下列方式都等价:
int 33
Int 21h
INT 21H

3.3 汇编指令集
一、数据传输指令
───────────────────────────────────────
它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据.
1. 通用数据传送指令.
MOV 传送字或字节.
MOVSX 先符号扩展,再传送.
MOVZX 先零扩展,再传送.
PUSH 把字压入堆栈.
POP 把字弹出堆栈.
PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
PUSHAD 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.
POPAD 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.
BSWAP 交换32位寄存器里字节的顺序
XCHG 交换字或字节.( 至少有一个操作数为寄存器,段寄存器不可作为操作数)
CMPXCHG 比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX )
XADD 先交换再累加.( 结果在第一个操作数里 )
XLAT 字节查表转换.
── BX 指向一张 256 字节的表的起点, AL 为表的索引值 (0-255,即
0-FFH); 返回 AL 为查表结果. ( [BX+AL]->AL )
2. 输入输出端口传送指令.
IN I/O端口输入. ( 语法: IN 累加器, {端口号│DX} )
OUT I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )
输入输出端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,
其范围是 0-65535.
3. 目的地址传送指令.
LEA 装入有效地址.
例: LEA DX,string ;把偏移地址存到DX.
LDS 传送目标指针,把指针内容装入DS.
例: LDS SI,string ;把段地址:偏移地址存到DS:SI.
LES 传送目标指针,把指针内容装入ES.
例: LES DI,string ;把段地址:偏移地址存到ES:DI.
LFS 传送目标指针,把指针内容装入FS.
例: LFS DI,string ;把段地址:偏移地址存到FS:DI.
LGS 传送目标指针,把指针内容装入GS.
例: LGS DI,string ;把段地址:偏移地址存到GS:DI.
LSS 传送目标指针,把指针内容装入SS.
例: LSS DI,string ;把段地址:偏移地址存到SS:DI.
4. 标志传送指令.
LAHF 标志寄存器传送,把标志装入AH.
SAHF 标志寄存器传送,把AH内容装入标志寄存器.
PUSHF 标志入栈.
POPF 标志出栈.
PUSHD 32位标志入栈.
POPD 32位标志出栈.

二、算术运算指令
───────────────────────────────────────
ADD 加法.
ADC 带进位加法.
INC 加 1.
AAA 加法的ASCII码调整.
DAA 加法的十进制调整.
SUB 减法.
SBB 带借位减法.
DEC 减 1.
NEC 求反(以 0 减之).
CMP 比较.(两操作数作减法,仅修改标志位,不回送结果).
AAS 减法的ASCII码调整.
DAS 减法的十进制调整.
MUL 无符号乘法.
IMUL 整数乘法.
以上两条,结果回送AH和AL(字节运算),或DX和AX(字运算),
AAM 乘法的ASCII码调整.
DIV 无符号除法.
IDIV 整数除法.
以上两条,结果回送:
商回送AL,余数回送AH, (字节运算);
或 商回送AX,余数回送DX, (字运算).
AAD 除法的ASCII码调整.
CBW 字节转换为字. (把AL中字节的符号扩展到AH中去)
CWD 字转换为双字. (把AX中的字的符号扩展到DX中去)
CWDE 字转换为双字. (把AX中的字符号扩展到EAX中去)
CDQ 双字扩展. (把EAX中的字的符号扩展到EDX中去)

三、逻辑运算指令
───────────────────────────────────────
AND 与运算.
OR 或运算.
XOR 异或运算.
NOT 取反.
TEST 测试.(两操作数作与运算,仅修改标志位,不回送结果).
SHL 逻辑左移.
SAL 算术左移.(=SHL)
SHR 逻辑右移.
SAR 算术右移.(=SHR)
ROL 循环左移.
ROR 循环右移.
RCL 通过进位的循环左移.
RCR 通过进位的循环右移.
以上八种移位指令,其移位次数可达255次.
移位一次时, 可直接用操作码. 如 SHL AX,1.
移位>1次时, 则由寄存器CL给出移位次数.
如 MOV CL,04
SHL AX,CL

四、串指令
───────────────────────────────────────
DS:SI 源串段寄存器 :源串变址.
ES:DI 目标串段寄存器:目标串变址.
CX 重复次数计数器.
AL/AX 扫描值.
D标志 0表示重复操作中SI和DI应自动增量; 1表示应自动减量.
Z标志 用来控制扫描或比较操作的结束.
MOVS 串传送.
( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )
CMPS 串比较.
( CMPSB 比较字符. CMPSW 比较字. )
SCAS 串扫描.
把AL或AX的内容与目标串作比较,比较结果反映在标志位.
LODS 装入串.
把源串中的元素(字或字节)逐一装入AL或AX中.
( LODSB 传送字符. LODSW 传送字. LODSD 传送双字. )
STOS 保存串.
是LODS的逆过程.
REP 当CX/ECX<>0时重复.
REPE/REPZ 当ZF=1或比较结果相等,且CX/ECX<>0时重复.
REPNE/REPNZ 当ZF=0或比较结果不相等,且CX/ECX<>0时重复.
REPC 当CF=1且CX/ECX<>0时重复.
REPNC 当CF=0且CX/ECX<>0时重复.

五、程序转移指令
───────────────────────────────────────
1>无条件转移指令 (长转移)
JMP 无条件转移指令
CALL 过程调用
RET/RETF过程返回.
2>条件转移指令 (短转移,-128到+127的距离内)
( 当且仅当(SF XOR OF)=1时,OP1<OP2 )
JA/JNBE 不小于或不等于时转移.
JAE/JNB 大于或等于转移.
JB/JNAE 小于转移.
JBE/JNA 小于或等于转移.
以上四条,测试无符号整数运算的结果(标志C和Z).
JG/JNLE 大于转移.
JGE/JNL 大于或等于转移.
JL/JNGE 小于转移.
JLE/JNG 小于或等于转移.
以上四条,测试带符号整数运算的结果(标志S,O和Z).
JE/JZ 等于转移.
JNE/JNZ 不等于时转移.
JC 有进位时转移.
JNC 无进位时转移.
JNO 不溢出时转移.
JNP/JPO 奇偶性为奇数时转移.
JNS 符号位为 "0" 时转移.
JO 溢出转移.
JP/JPE 奇偶性为偶数时转移.
JS 符号位为 "1" 时转移.
3>循环控制指令(短转移)
LOOP CX不为零时循环.
LOOPE/LOOPZ CX不为零且标志Z=1时循环.
LOOPNE/LOOPNZ CX不为零且标志Z=0时循环.
JCXZ CX为零时转移.
JECXZ ECX为零时转移.
4>中断指令
INT 中断指令
INTO 溢出中断
IRET 中断返回
5>处理器控制指令
HLT 处理器暂停, 直到出现中断或复位信号才继续.
WAIT 当芯片引线TEST为高电平时使CPU进入等待状态.
ESC 转换到外处理器.
LOCK 封锁总线.
NOP 空操作.
STC 置进位标志位.
CLC 清进位标志位.
CMC 进位标志取反.
STD 置方向标志位.
CLD 清方向标志位.
STI 置中断允许位.
CLI 清中断允许位.

六、伪指令
───────────────────────────────────────
DW 定义字(2字节).
PROC 定义过程.
ENDP 过程结束.
SEGMENT 定义段.
ASSUME 建立段寄存器寻址.
ENDS 段结束.
END 程序结束.


B34层 发表时间: 05-05-17 12:00

回复: 286 [unique]   版主   登录
3.4 再谈寄存器和内存的区别
  第零讲说到“寄存器在CPU中。内存在内存条中。前者的速度比后者快100倍左右。后面的程序要求每条指定要么没有内存数据,要么在有一个寄存器的参与下有一个内存数据。(也就是说,不存在只访问内存的指令)。”
  寄存器是在CPU中的存储器,而内存是在内存条中的存储器。CPU访问寄存器,只需要通过微指令直接就可以访问,而访问内存则要先经过总线,再由总线到达内存控制器,读到某单元的内存数据后放上总线,再传到CPU中,CPU才能使用。
  8086系列计算机的寄存器,共有14个,每个都是十六位的。
AX,BX,CX,DX,SP,BP,SI,DI,CS,DS,SS,ES,IP,FLAGS。
其中前四位,每个可以单位再分成两个,AX=AH+AL,BX=BH+BL,CX=CH+CL,DX=DH+DL。这些分开的每个都是8位的。
  这个分开不要理解成平时语言中的分开,你可以理解为AX是由AH和AL组合成的,你给AL付值,就意味着同时给AX的低半部付值。你给AX付值,就意味着同时改变AH和AL。这样作的好处是你可以更灵活地控制这个寄存器。


3.5 指令说明
看了3.3的指令集和3.4的寄存器,是不是已经了,或者了?不要急,上面的东西虽然多,我也没让你一下学会,(其实有些永远也不会似乎也不是什么大不了的事)。为了应付看的懂我后面所说的,我把其中的指令挑几个重点的,你必须要记住,其它的慢慢学吧。

1数据传输指令。
mov A,B
注意不是move,这个指令是把B中的数据复制给A,(B中仍保存原状)。这里的A和B可以是寄存器,可以是内存。但可以同时是寄存器,不能同时是内存。比如
mov ax,100 ;这是对的,注意100在这里叫立即数,但这个数在编译系统编译成exe的时候保存在内存中。如果学过别的高级语言,你就可以理解为这就是赋值语句 Let ax=100/ax:=100;/ax=100。
2 伪指令
伪指令就是不是真的指令,但他同时又是指令。之所以说这样矛盾的话,是因为伪指令不是机器语言的一部分,而是汇编语言的一部分,是你告诉汇编的编译系统如何去作。
string DB '这是我的第一个汇编语言程序$'
上面一行指令中,DB就是伪指令,他的作用就是告诉编译程序,把后面一些数据或字符串放到内存中。当然对于exe来说,已在内存中了,就不用“告诉”了。(这就是为什么叫伪指令)。string是你给这段内存起的名字,如果你不需要这段内存,不起名字也可以,但如果后面要用,当然要加上这个名字。'这是我的第一个汇编语言程序$'这个就是要处理的数据,当然你也可以换成别的内容,但需要注意的是,要以'$'结尾,这是汇编的约写,即:只是到了$,就认为字符串结束,否则就一直向下找,直到找到一个$为止。所以这就要求你的字符串中不能有'$',如果必须有,再换别的处理方式,后面再说。
3 地址传送指令
Lea A,string
前面已经定义了string,后面要把地址找到,就要用到lea指令。lea是把字符串的地址给A这个寄存器中,A当然可以上前面提到的任意寄存器。注意地址和内容的区别。如果是内容就是把string的字符串给A了。(当然这也不成立,一个字符串有很多字节,而一个寄存器只有两个字节)。
那么从上面也看到了,string代表一个地址,lea把这个地址给了A,那这个地址到底在哪里呢?事实上这不重要,就象你要把某书店买书,这个书店在哪并不是最重要的,有没有你要的书才是最重要的。所以你前面标出string,后面引用就行了,至于这个地址到底在哪是编译程序的事,不是你的事。

4 运算指令
ADD A,N
这个很容易理解吧,寄存器A加上N,把和仍存在A中。类似于高级语言中的let a=a+n/a:=a+n/a+=n。

5 串操作指令
记住串操作指令表面很复杂,其实很简单。
因为他就象一个复杂的数学公式一样简单,你所要记住的就是公式的格式,使用时具体套用即可。
从一个地址到另一个地址的复制需要注意的是:
*把源串段地址给DS。
*把源串编址给SI。
*把目的串段址给ES。
*把目的串偏址给DI。
*把要复制的个数给CX,这里可不考虑$了。
*把FLAG中的方向标志标志你要的方向,一个是顺向,另一个是逆向。
*发送loop movs,scans等命令。


6 转移指令
  记住:无条件转移指令 jmp。等于转 jz,不等于时转jnz

7 中断指令
  int 中断号,注意进制,默认是十进制,所以十六进制就加h。

好了,上面的指令变成七八个了,这你不能嫌多了吧,如果再嫌多就不要继续向下看了。



B35层 发表时间: 05-05-17 12:48

回复: 286 [unique]   版主   登录
第四讲 汇编程序

4.1 汇编程序框架

data SEGMENT '数据段,编程者可以把数据都放到这个段里
....数据部分
'数据格式是: 标识符 db/dw 数据。
data ENDS'数据段结束处。

edata SEGMENT '附加数据段,编程者可以把数据都放到这个段里
....附加数据部分
edata ENDS'附加数据段结束处。

code SEGMENT'代码段,实际的程序都是放这个段里。
      ASSUME CS:code,DS:data,ES:edata '告诉编译程序,data段是数据段DS,code段是代码段CS
start:MOV AX,data '前面的start表示一个标识位,后面用到该位,如果用不到,就可以不加
      MOV DS,AX '这一句与上一行共同组成把data赋值给DS。段寄存器.
      MOV AX,edata
      MOV ES,AX '与前一句共同组成edata->ES
      .......程序部分
      MOV AX,4C00h'程序退出,该句内存由下一行决定。退出时,要求ah必须是4c。
      INT 21h
code ENDS'代码段结束。
END start'整个程序结束,并且程序执行时由start那个位置开始执行。


上面就是一个程序的框架结构。在这个结构中,有三个段,DS,ES,CS。这三个段分别存数据,附加数据,代码段。

4.2 编写我们的Hello,world思路。
开始编写我们的第一个程序。
程序要求:显示一个“Hello,Mr.286.”怎么样?
思路:
1 要显示一个字符串,根据前面我让你们记的七八个指令够吗?答案是:不仅够,而且还用不完。
首先定义一下总可以吧。

hellostr db 'Hello,Mr.286.$'
最后的$不要忘了。

2 首先要考虑的问题就是找中断,找到合适的中断,该中断就能帮我们完成这个显示任务。我找到(在哪找到的,怎么找到的,别问我,到网上或书上都能找到):
-------------------------------------------
中断INT 21H功能09H

功能描述: 输出一个字符串到标准输出设备上。如果输出操作被重定向,那么,将无法判断磁盘已满
入口参数: AH=09H
DS:DX=待输出字符的地址
说明:待显示的字符串以’$’作为其结束标志
出口参数: 无
-------------------------------------------
由上面看到,我们所需要作的就是把DS指向数据段,DX指向字符串的地址,AH等于9H,调用21h中断。
mov ds,数据段地址
lea dx,hellostr 'hellostr已在前面1中定义了。
mov ah,9h
int 21h。
由于只要在调用int 21h之前把准备的东西准备齐就行了,所以int 21h前面三行的顺序并不重要。

3 退出程序,运行完总要退出呀。再查中断手册
--------------------------------------------
中断INT 21H功能4CH

功能描述: 终止程序的执行,并可返回一个代码
入口参数: AH=4CH
AL=返回的代码
出口参数: 无

--------------------------------------------
mov ah,4Ch
mov al,0
int 21h

mov ax,4c00h
int 21h
这里需要说明的是返回代码有什么用,返回给谁?返回给操作系统,因为是操作系统DOS调用的这个程序,这个返回值可以通过批处理中的errorlevel得到,这里不多说明,实际上操作系统很少处理这一值,因此al你随便写什么值影响都不大。

4.3 程序实现
data SEGMENT
msg DB 'Hello, Mr.286.$'
data ENDS

code SEGMENT
      ASSUME CS:code,DS:data
start:MOV AX,data
      MOV DS,AX
      lea dx,msg
      mov ah,9h
      int 21h
      MOV AX,4C00h
      INT 21h
code ENDS
END start

4.4 编译运行。
把上面程序保存成hello286.asm后,就可以编译运行了。进入DOS,进入汇编目录,如果还没下载,到前面找下载地址。

=================================================
E:\Download\Masm>masm hello286.asm
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.

Object filename [hello286.OBJ]:
Source listing  [NUL.LST]:
Cross-reference [NUL.CRF]:

  50408 + 415320 Bytes symbol space free

      0 Warning Errors
      0 Severe  Errors
说明:上面连续三个回车,表示我要的都是默认值。下面是零个警告,零个严重错误,(当然了,我的程序还敢错吗?)

E:\Download\Masm>link hello286

Microsoft (R) Overlay Linker  Version 3.60
Copyright (C) Microsoft Corp 1983-1987.  All rights reserved.

Run File [HELLO286.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
LINK : warning L4021: no stack segment

说明:三个回车仍要默认,后面有个警告,没有栈段,这个没关系,没有的话系统会自动给一个()。

E:\Download\Masm>hello286
Hello, Mr.286.
说明:运行成功。
E:\Download\Masm>
===================================================


B36层 发表时间: 05-05-17 13:27

回复: 286 [unique]   版主   登录
4.4 深度思考
4.4.1 是不是数据必须放数据段,代码必段放代码段呢?
答,代码必段放代码段,否则你怎么执行呀?但数据也可以放到代码段,只是程序要作修改。
code SEGMENT
      ASSUME CS:code,DS:data
      msg DB 'Hello, Mr.286.$'
start:MOV AX,data
      MOV DS,AX
      lea dx,msg
      mov ah,9h
      int 21h
      MOV AX,4C00h
      INT 21h
code ENDS
END start
编译后仍然可以。
4.4.2 我编的程序在内存中是什么样子的呢?
------------------------------------------------------------------------
E:\Download\Masm>debug hello286.exe
-u
1420:0000 B81F14        MOV    AX,141F
1420:0003 8ED8          MOV    DS,AX
1420:0005 8D160000      LEA    DX,[0000]
1420:0009 B409          MOV    AH,09
1420:000B CD21          INT    21
1420:000D B8004C        MOV    AX,4C00
1420:0010 CD21          INT    21
1420:0012 FF362421      PUSH    [2124]
1420:0016 E87763        CALL    6390
1420:0019 83C406        ADD    SP,+06
1420:001C FF362421      PUSH    [2124]
-d 141f:0000 L20
141F:0000  48 65 6C 6C 6F 2C 20 4D-72 2E 32 38 36 2E 24 00  Hello, Mr.286.$.
141F:0010  B8 1F 14 8E D8 8D 16 00-00 B4 09 CD 21 B8 00 4C  ............!..L
-q

E:\Download\Masm>
------------------------------------------------------------------------------
上面是什么呀?,还记得前面说的吗?
1420:0000 B81F14        MOV    AX,141F
  |  |      |          |          |
段址:偏址 机器语言      mov指令 把段地址的地址(141f)赋值给AX寄存器。

1420:0012后面的是垃圾数据,不用管它,把上面程序与源程序作一个比较,看有什么不用,差别在于把标号语言转成实际地址了。
程序前两行一执行,数据段地址就变成了141f,而那个字符串偏移地址在0000,由(LEA    DX,[0000]看出),所以我用-d 141f:0000 L20(后面L20表示只显示20个字节),就能把段地址显示出来了。
所以刚才的程序在内存中就变成了:
141f:0000 Hello, Mr.286.$  ----->这是段地址里的内存
1420:0000 B81F14        MOV    AX,141F  ------>这是代码段里的内存。data变成了实际地址
1420:0003 8ED8          MOV    DS,AX
1420:0005 8D160000      LEA    DX,[0000] ------>偏址变成了0000,因为实际上msg也就是从头开始的。当然是0了。
1420:0009 B409          MOV    AH,09    ------->注意Debug里,默认的是十六进制
1420:000B CD21          INT    21
1420:000D B8004C        MOV    AX,4C00
1420:0010 CD21          INT    21



B37层 发表时间: 05-05-17 13:48

回复: qwhacker [qwhacker]      登录
4.4深度思考中的这个程序没有定义数据段;不会通过编译;
code SEGMENT
      ASSUME CS:code,DS:data
      msg DB 'Hello, Mr.286.$'
start:MOV AX,data
      MOV DS,AX
      lea dx,msg
      mov ah,9h
      int 21h
      MOV AX,4C00h
      INT 21h
code ENDS
END start
应该在前面添加
data segment
data ends

B38层 发表时间: 05-05-24 10:41

回复: qwhacker [qwhacker]      登录
哪个程序在我的机子的内存上怎么是这个样子?
0B49:000F B8490B        MOV    AX,0B49
0B49:0012 8ED8          MOV    DS,AX
0B49:0014 8D160000      LEA    DX,[0000]
0B49:0018 B409          MOV    AH,09
0B49:001A CD21          INT    21
0B49:001C B8004C        MOV    AX,4C00
0B49:001F CD21          INT    21
0B49:0021 C786FEFE0000  MOV    WORD PTR [BP+FEFE],0000
0B49:0027 EB05          JMP    002E
0B49:0029 90            NOP
0B49:002A FF86FEFE      INC    WORD PTR [BP+FEFE]
0B49:002E A15607        MOV    AX,[0756]
怎么根286的不一样?

B39层 发表时间: 05-05-24 10:52

回复: ALLyeSNO [allyesno]   论坛用户   登录
支持

B40层 发表时间: 05-05-30 01:45

回复: johns [johns]   论坛用户   登录
我学来学去都学不会,因为我的高数很差 ,很差!!!!!!!

B41层 发表时间: 05-07-27 00:13

回复: zxdlovedjw [zxdlovedjw]   论坛用户   登录
  我软盘上的文件被我误删了 我现在想恢复出来 我把软盘上的文件根目录找到 把文件名的第一个字母也改回来了 文件也恢复出来了 可是文件用不成  文件大小为100KB 左右 请老师帮忙




B42层 发表时间: 05-07-28 18:06

回复: wymaomao [wymaomao]   论坛用户   登录
真晕了,看到文字就头痛!
弄点视频的教程吧老大!

B43层 发表时间: 05-08-04 07:55

回复: jueguzi [jueguzi]   论坛用户   登录
哪个程序在我的机子的内存上怎么是这个样子?
0B49:000F B8490B        MOV    AX,0B49
0B49:0012 8ED8          MOV    DS,AX
0B49:0014 8D160000      LEA    DX,[0000]
0B49:0018 B409          MOV    AH,09
0B49:001A CD21          INT    21
0B49:001C B8004C        MOV    AX,4C00
0B49:001F CD21          INT    21
0B49:0021 C786FEFE0000  MOV    WORD PTR [BP+FEFE],0000
0B49:0027 EB05          JMP    002E
0B49:0029 90            NOP
0B49:002A FF86FEFE      INC    WORD PTR [BP+FEFE]
0B49:002E A15607        MOV    AX,[0756]
怎么根286的不一样?

你的机子当然不是286的了,所以你的数据段和他的不是一个东东了!
-d 0B49:0000 20 就ok 了!


B44层 发表时间: 05-08-05 15:32

回复: angus [angus]   论坛用户   登录
强烈支持楼主!
看了你写的比看10遍书还管用啊!
继续啊!!!!

B45层 发表时间: 05-08-05 15:33

回复: angus [angus]   论坛用户   登录
继续啊286!


B46层 发表时间: 05-08-07 19:48

回复: lez91 [lez91]   论坛用户   登录
能不能整理成电子图书提供给大家下载。

B47层 发表时间: 05-08-08 10:18

回复: zhlip [zhlip]   论坛用户   登录
268,,,这样不好下载呀
搞点电子书吗?像这样的资料太少了,
但不要怕,我会帮你发扬光大的


B48层 发表时间: 05-08-08 22:13

回复: jalen1130 [jalen1130]   论坛用户   登录
偶终于找到一个关于汇编的坛子了


先潜水学习在说!

B49层 发表时间: 05-08-10 11:27

回复: battle [battle]   论坛用户   登录
286大哥继续啊......
没想到就这么点代码..就可以查看系统时间..还可以改时间呢.....
年月日还是不知道怎么打出来..望指导...
code SEGMENT
ASSUME CS:code
start:
MOV AH,2ah
int 21h
mov ah,9h
int 21h
code ENDS
END start

B50层 发表时间: 05-08-12 02:01

回复: baihai [baihai]   论坛用户   登录
code SEGMENT
ASSUME CS:code
start:
MOV AH,2ah
int 21h
mov ah,9h  ―>是显示字符串,况且你即没定义存储区也没给地址,所以打印不出来
int 21h
code ENDS
END start


*********************************************************

mov ah,9h 
int  21h
是显示字符串,况且你即没定义存储区也没给地址,所以打印不出来.
能打出来的程序如下,自己慢慢看,有时间多看些汇编书。自己找结果
比别人教更有意义。我的QQ:476700330
有问题大家探讨。


; 汇编语言 取当前日期的程序
;

DATA    SEGMENT  PARA                    ;定义数据段
DATE1  DB        'Current date is $'   
DATE2  DB        '01-01-1980',0DH,0AH,'$'
TIME1  DB        'Current time is $'
TIME2  DB        '00:00:00',0DH,0AH,'$'
DATA    ENDS

STACK  SEGMENT  PARA  STACK 'STACK'      ;定义堆栈段
        DW        100H DUP(?)
STACK  ENDS
   
CODE    SEGMENT  PARA                      ;定义程序段
        ASSUME    CS:CODE,DS:DATA,SS:STACK
MAIN    PROC      FAR
        MOV      AX,DATA
        MOV      DS,AX

        LEA      DX,DATE1              ;数据段日期提示数据显示输出
        MOV      AH,9                  ;Dos功能调用(21H)
        INT      21H
   
        MOV      AH,04H                ;Dos功能调用,4H 获得系统日期     
        INT      1AH                  ;Dos功能调用(1AH)

        MOV      AX,CX                ;年份在CX中
        MOV      CX,4                  ;大循环4次
        LEA      SI,DATE2+6
LP1:    PUSH      CX

        MOV      CL,4                  ;小循环4次
        ROL      AX,CL

        MOV      BL,AL
        AND      BL,0FH
        OR        BL,30H
        MOV      [SI],BL
        INC      SI
        POP      CX
        LOOP      LP1
   
        MOV      AX,DX                ;日、月在DX中
        MOV      CX,4                ;大循环4次
        LEA      SI,DATE2
LP2:    PUSH      CX

        MOV      CL,4                ;小循环4次
        ROL      AX,CL

        MOV      BL,AL
        AND      BL,0FH
        OR        BL,30H
        MOV      [SI],BL
        INC      SI
        POP      CX
        CMP      CX,3                ;跳开日期分隔符‘ - ’
        JNZ      LP2_1
        INC      SI
LP2_1:  LOOP      LP2

        LEA      DX,DATE2            ;系统日期输出 
        MOV      AH,9
        INT      21H

        LEA      DX,TIME1            ;数据段时间提示数据显示输出
        MOV      AH,9                ;Dos功能调用(21H)
        INT      21H

        MOV      AH,2                ;Dos功能调用,2H 获得系统时间
        INT      1AH                ;Dos功能调用(1AH)
       
        MOV      AX,CX              ;CX中为分钟和小时
        MOV      CX,4
        LEA      SI,TIME2           
LP3:    PUSH      CX
        MOV      CL,4
        ROL      AX,CL
        MOV      BL,AL
        AND      BL,0FH
        OR        BL,30H
        MOV      [SI],BL
        INC      SI
        POP      CX
        CMP      CX,3              ;跳开时间分隔符‘ : ’
        JNZ      LP3_1
        INC      SI
LP3_1:  LOOP      LP3

        PUSH      DX
        MOV      CL,4
        SHR      DH,CL          ;DH中为秒钟
        ADD      DH,30H
        MOV      TIME2+6,DH      ;秒钟的前位
        POP      DX
        AND      DH,0FH
        ADD      DH,30H
        MOV      TIME2+7,DH      ;秒钟的后位

        LEA      DX,TIME2        ;系统时间输出
        MOV      AH,9
        INT      21H

        MOV      AX,4C00H
        INT      21H
MAIN    ENDP
CODE    ENDS
        END      MAIN

********************************************************
程序2
; 汇编语言 取当前日期的程序
;
DATA    SEGMENT    PARA                    ;定义数据段
DATE    DB        'Current date is :$'   
TIME    DB        'Current time is :$'
DATA1  DW        ?
TIME1  DW        ?
CLTR    DB        0DH,0AH,'$'
DATA    ENDS
STACK  SEGMENT  PARA  STACK 'STACK'      ;定义堆栈段
        DW        100H DUP(?)
STACK  ENDS
CODE    SEGMENT  PARA                      ;定义程序段
        ASSUME    CS:CODE,DS:DATA,SS:STACK
MAIN    PROC      FAR
        MOV      AX,DATA
        MOV      DS,AX

        LEA      DX,DATE              ;数据段日期提示数据显示输出       
        CALL      OUTP

        MOV      AH,04H                ;Dos功能调用,4H 获得系统日期     
        INT      1AH                  ;Dos功能调用(1AH)

        MOV      AX,CX                ;年份在CX中
        MOV      BX,AX
        MOV      DATA1,DX              ;日期在DX中

        MOV      AL,BH                ;年份上半部分显示输出 
        MOV      CL,4
        ROR      AL,CL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        MOV      AL,BH
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
       
        MOV      AL,BL                  ;年份下半部分显示输出
        MOV      CL,4
        ROR      AL,CL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        MOV      AL,BL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST

        MOV      DL,'-'
        CALL      LIST
       
        MOV      AX,DATA1                ;月份显示输出
        MOV      BL,AH 
        MOV      AL,BL
        MOV      CL,4
        ROR      AL,CL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        MOV      AL,BL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        MOV      DL,'-'
        CALL      LIST

        MOV      AX,DATA1                ;日期显示输出
        MOV      BL,AL
        MOV      CL,4
        ROR      AL,CL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        MOV      AL,BL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        CALL      CR

        LEA      DX,TIME            ;数据段时间提示数据显示输出
        CALL      OUTP                ;Dos功能调用(21H)

        MOV      AH,2                ;Dos功能调用,2H 获得系统时间
        INT      1AH                ;Dos功能调用(1AH)
       
        MOV      AX,CX              ;CX中为分钟和小时       
        MOV      BX,AX
        MOV      TIME1,DX            ;秒钟在DX中

        MOV      AL,BH              ;小时半部分显示输出 
        MOV      CL,4
        ROR      AL,CL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        MOV      AL,BH
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        MOV      DL,':'
        CALL      LIST

       
        MOV      AL,BL              ;分钟部分显示输出
        MOV      CL,4
        ROR      AL,CL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        MOV      AL,BL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST

        MOV      DL,':'
        CALL      LIST
 
        MOV      AX,TIME1            ;秒钟部分显示输出
        MOV      BL,AH
        MOV      AL,BL
        MOV      CL,4
        ROR      AL,CL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST
        MOV      AL,BL
        AND      AL,0FH
        ADD      AL,30H
        MOV      DL,AL
        CALL      LIST

        MOV      AX,4C00H
        INT      21H
MAIN    ENDP
outp proc near                ;定义字符串输出子程序
    mov  ah,9
    int  21h
    ret
outp endp
cr  proc near                ;定义字换行子程序
    lea  dx,cltr
    call outp
    ret
cr  endp
list proc near                ;定义单字符输出子程序
    mov  ah,2
    int  21h
    ret
list endp
CODE ENDS
    END MAIN




B51层 发表时间: 05-09-14 09:45

回复: baihai [baihai]   论坛用户   登录
拷贝后,自己整理不就得了吗?

B52层 发表时间: 05-09-14 10:35

回复: baihai [baihai]   论坛用户   登录
code SEGMENT
      ASSUME CS:code,DS:data
      msg DB 'Hello, Mr.286.$'――>我的机子肯定不行,记得老师提过。试过N*N次,上当!
start:MOV AX,data
      MOV DS,AX
      lea dx,msg
      mov ah,9h
      int 21h
      MOV AX,4C00h
      INT 21h
code ENDS
END start

应改为:

data segment
msg DB 'Hello, Mr.286.$'
data ends
code SEGMENT
      ASSUME CS:code,DS:data 
start:MOV AX,data
      MOV DS,AX
      lea dx,msg
      mov ah,9h
      int 21h
      MOV Ah,4Ch
      INT 21h
code ENDS
END start

有时间多看些汇编书。自己找结果
比别人教更有意义。我的QQ:476700330
有问题大家探讨。




[此贴被 baihai(baihai) 在 09月16日11时27分 编辑过]

B53层 发表时间: 05-09-14 11:05

回复: 286 [unique]   版主   登录
最近太忙,没时间看,没想到还真有不少人看这个贴子。续写短时间内可能性不大。不过,我就针对上面几个回答作一个回复。

1 那个只有代码段的程序。很多看客估计已经试过,但没试成功,部分人还给出了修改意见,但我看了所有的修改,都有问题,改后当然是通过了,但结果是不对的,也与我说的数据段代码段合并有冲突。
其原因很简单,因为我很多东西是在线写的,部分程序也没有去调试。错就错在一个符号上。现在我把正确的贴出,大家可以试试。
code SEGMENT
      ASSUME CS:code    ------------->改处一,既然不要数据段了,也没必要要DS了。当然这个地步是小地方,前面说过ASSUME只是告诉其作用,多写DS:DATA也不算错。
      msg DB 'Hello, Mr.286.$'
start:MOV AX,code        ------------->改处二:这个地方才是关键,原来是DATA,现在合并了,应该改为CODE。
      MOV DS,AX
      lea dx,msg
      mov ah,9h
      int 21h
      MOV AX,4C00h
      INT 21h
code ENDS
END start

2 很多人说DEBUG一项显示的内容与我显示的不一样。一个程序被加载到内存中,此前运行过的程序个数和顺序是不一样的,一般来说,程序被加载到内存最低端,因此新程序的段地址不一样是正常的。也就是说你们显示结果和我的显示结果“不完全”一样是正常的。比如。
|------A程序-----|------B程序-----|-----你的程序-----|
|------A程序-----|------你的程序-----|-----C程序-----|
上面两种情况下,两个“你的程序”首地址当然会不一样,但偏移地址肯定是一样的。

------------------------引用我此前的内容-------------------------------
E:\Download\Masm>debug hello286.exe
-u
1420:0000 B81F14        MOV    AX,141F
1420:0003 8ED8          MOV    DS,AX
1420:0005 8D160000      LEA    DX,[0000]
1420:0009 B409          MOV    AH,09
1420:000B CD21          INT    21
1420:000D B8004C        MOV    AX,4C00
1420:0010 CD21          INT    21
1420:0012 FF362421      PUSH    [2124]
1420:0016 E87763        CALL    6390
1420:0019 83C406        ADD    SP,+06
1420:001C FF362421      PUSH    [2124]
-d 141f:0000 L20
141F:0000  48 65 6C 6C 6F 2C 20 4D-72 2E 32 38 36 2E 24 00  Hello, Mr.286.$.
141F:0010  B8 1F 14 8E D8 8D 16 00-00 B4 09 CD 21 B8 00 4C  ............!..L
-q

E:\Download\Masm>
---------------------------------------引用结束----------------------------
在上述引用框内,你的内存映象中段址和我的内存映象中,你的代码段段址可以和我的1420不一样,你的数据段地址可以和我的141F不一样。1420:0012 以后的内容可以不一样,因为那已是咱们的程序之外的随机数据。但其它的应该一样,包括MOV    AX,141F应该是0000,MOV    AX,4C00应该是000D。
有些书为了避免大家产生误会,常常写成:
CS:0000 B81F14        MOV    AX,141F
CS:0003 8ED8          MOV    DS,AX
CS:0005 8D160000      LEA    DX,[0000]
CS:0009 B409          MOV    AH,09
CS:000B CD21          INT    21
CS:000D B8004C        MOV    AX,4C00
CS:0010 CD21          INT    21

B54层 发表时间: 05-09-16 17:38

回复: 286 [unique]   版主   登录
那个日期显示部分,我现在下班了。,没时间编写了。不过你们可以通过这种方式验证。

E:\Download\Masm>debug
-a
1360:0100 mov ah,2a
1360:0102 int 21
1360:0104 int 3    ---------------》INT 3 就是程序运行到这里停一下。
1360:0105
-g=100

AX=2A05  BX=0000  CX=07D5  DX=0910  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1360  ES=1360  SS=1360  CS=1360  IP=0104  NV UP EI PL NZ NA PO NC
1360:0104 CC            INT    3
-
2A功能描述为: 取系统日期
入口参数: AH=2AH
出口参数: CX=年(1980~2099),DH=月(1~12),DL=日(1~31)
AL=星期几(0=Sunday,1=Monday,...)
看看上面的结果,DH=09,也就是9月份。DL=10H=16,记得H是什么意思吗?就是十六进制,十进制就是16,今天是9月16日。后天是中秋节。AL=05,今天是周五。CX=07D5H=2005年。
具体怎么显示出来,下次再说吧。

B55层 发表时间: 05-09-16 17:46

回复: GuessMe [guessme]   论坛用户   登录
大家来讨论汇编这么热闹,我也来凑乎凑乎。下面的是我在www.cnxhacker.com发的帖子,下面转到这里来,大家来看看。
-------------------------------------------------------------------------------------
刚刚学习了8086/8088汇编语言,发现寻址方式非常重要,于是做了一个小总结,请各位笑纳。
概念:
    1.指令集:cpu能够执行的指令的集合。
    2.指令:cpu所能够执行的操作。
    3.操作数:参加指令运算的数据。
    4.寻址方式:在指令中得到操作数的方式。
现在就重点讨论寻址方式,说白了也就是cpu怎么样从指令中得到操作数的问题。另外再强调一点操作数还分种类:
1)数据操作数:全都是在指令当中参加操作的数据。
    1.立即操作数:它在指令中直接给出。
    2.寄存器操作数:它被放到寄存器中。
    3.存储器操作数:当然在存储器也就是内存中。
    4.i/o操作数:它在你给出的i/o端口中。
2)转移地址操作数:在指令当中不是参加运算或被处理的数据了,而是转移地址。
还可以按照下面分类方式:
1)源操作数src
2)目的操作数dst
源操作数都是指令当中的第2个操作数,在执行完指令后操作数不变。而目的操作数是指令当中的第1个操作数,在执行完操作指令后被新的数据替代。
我们就围绕这几种操作数,也就是操作数所在的位置展开讨论。
先说数据操作数,它分3大类共7种。
1)立即数寻址方式:是针对立即操作数的寻址方式。在指令当中直接给出,它根本就不用寻址。
例1:mov ax,1234h
    mov [bx],5678h
在这里1234h和5678h都是立即操作数,在指令当中直接给出。
2)寄存器寻址方式:是针对寄存器操作数的寻址方式,它在寄存器中我们就用这中方式来找到它。
例2:mov  bx,ax
    mov  bp,[si]
在这里ax,bx,ds都算是寄存器寻址,例1中的ax也是寄存器寻址方式。
3)存储器寻址方式:针对在内存中的数据(存储器操作数)都用这种方式来寻找,一共有5种(这是我自己的说法,便于记忆)。
不得不提及以下的概念:由于8086/8088的字长是16bit,能够直接寻址2的16次方也就是64kb,而地址总线是20bit,能够直接寻址2的20次方也就是1M空间,所以把内存分为若干个段,每个段最小16byte(被称为小节),最大64kb,它们之间可以相互重叠,这样一来内存就被分成以16byte为单元的64k小节,cpu就以1小节为单位寻址:在段寄存器中给出段地址(16bit),在指令当中给出段内偏移地址(16bit),然后把段地址左移4bit再与偏移地址求和就得到数据在内存当中的实际物理地址了,因而可以找到数据。
1.存储器直接寻址方式:在指令当中以  [地址]  的方式直接给出数据所在内存段的偏移地址。
例3:mov  ax,es:[1234h]
    mov  dx,VALUE
    mov  dx,[VALUE]
在这里[1234h]和VALUE就是在指令中直接给出的数据所在内存段的偏移地址(16bit)。
VALUE是符号地址,是用伪指令来定义的,它代表一个在内存中的数据(也就是它的名字)。es:是段前缀符,用来指出段地址,在这之前应该将段地址添入段中,本例中是es,默认是ds,也就是不需给出。应该注意  [地址]  与立即寻址的区别,在直接给出的数据两边加  []  表示存储器直接寻址,以区别立即寻址。另外  VALUE=[VALUE]。
2.寄存器间接寻址:不是在指令中直接给出数据在内存中的偏移地址,而是把偏移地址放到了寄存器中。
例4:mov  ax,[bx]
这里[bx]就是寄存器间接寻址,bx中应方入段内偏移地址。其中:若使用bx,si,di默认段地址为ds,若使用bp则默认段地址为ss,并且允许段跨越,也就是加段前缀符。注意:在寄存器两边加  []  以与寄存器寻址区别。
3.寄存器间接相对寻址:偏移地址是bx,bp,si,di中的内容再与一个8bit或16bit 的位移量之和。
例5:mov  ax,[bx]+12h
    mov  ax,[si]+5678h
    mov  ax,[bp]+1234h
在这里[bx]+12h,[si]+5678h,[bp]+1234h都是寄存器间接相对寻址。12h是8bit位移量,1234h和5678h是16bit位移量。若使用bx,si,di则默认段寄存器是ds,若使用bp则默认段寄存器是ss,并且允许段跨越。
4.基址变址寻址:偏移地址是一个基址寄存器和一个变址寄存器内容的和,既:bx或bp中的一个与si或di中的一个求和而得到。
例6:mov  ax,[bx+si]
    mov  ax,[bp+di]
上面[bx+si]和[bp+di]都是基址变址寻址。若使用bx做基址寄存器则默认段地址为ds,若使用bp为基址寄存器则默认段为ss,允许段跨越。
5.基址变址相对寻址:偏移量是一个基址寄存器一个变址寄存器只和再与一个8bit或一个16bit位移量只和得到。
例7:mov  ax,[bx+si]+12h
    mov  ax,[bp+di]+1234h
[bx+si]+12h和[bp+di]+1234h就是基址变址相对寻址。若使用bx做基址寄存器则默认段是ds,若使用bp做基址寄存器则默认段为ss。允许段跨越。

下面是转移地址操作数的寻址方式:
1)段内直接转移
    1.段内直接短转移:cs(代码段)内容不变,而ip(指令指针寄存器)内容由当前ip内容+(-127~127),在指令中直接给出。
例8:jmp  short  SHORT_NEW_ADDR
其中,short是段内短转移的操作符,用以指出是转移到当前位置前后不超过±127字节的地方。而NEW_ADDR是要转移到的符号地址,它的位置应该在当前ip指针所在偏移地址不超过
±127的地方。否则语法出错。
    2.段内直接近转移:cs内容不变,而ip内容由当前ip内容+(-32767~32767),在指令中直接给出。
例9:jmp  near  ptr  NEAR_NEW_ADDR
其中near ptr是段内近转移的操作符,用以指出转移到当前位置前后不超过±32767的地方。NEAR_NEW_ADDR是要转移到的符号地址。
2)段内间接转移:cs的内容不变,而ip的内容放在寄存器中或者存储器中给出。
例10:jmp  bx
      jmp  word  ptr  [bx]+1234h
这种寻址方式是在寄存器或存储器中找到要转移到的地址,而地址是16bit的,因而寄存器必须为16bit,如:bx,我们用word  ptr来指定存储器单元也是16bit的。注意:它是间接的给出,只能使用类似于数据操作数中的除立即寻址以外的6种寻址方式(就在上面)。
3)段间直接寻址:cs和ip的内容全都变化,由指令当中直接给出要转移到的某一个段内的某一个偏移地址处。
例11:jmp  1234h:5678h
      jmp  far  ptr  NEW_ADDR
1234h送入cs中作为新的段地址,5678h送入ip中作为新的偏移地址。far  ptr是段间直接转移操作符,NEW_ADDR是另外一个段内的偏移地址,在这个指令中把NEW_ADDR的段地址送入cs(不用你给出),把它的段内偏移地址送入ip中作为新的偏移地址。
4)段间间接寻址:cs和ip的内容全变化,由指令当中给出的一个4字节连续存储单元,其中低2字节送入ip作为偏移地址,高2字节送入cs作为段地址。
例12:jmp  dword  ptr  [bx][si]+1234h
      jmp  dword  ptr  [1234h]
      jmp  dword  ptr  [si]
dword  ptr是双字(4个字节连续存储单元)操作符,用来指出下面的存储单元是4个字节的。由于它是4个字节的,所以只能使用类似于数据操作数中的存储器寻址方式(共5种,还记得吗?)。


另外作为特殊的寻址方式还有三种:I/O寻址,串寻址,隐含寻址。它们都分别针对I/O指令,串操作指令以及无操作数的指令,而且都比较简单,读者自行总结。


到此为止说明了8086/8088cpu中的所有寻址方式,我这里只是个总结,具体的细节还要大家自己钻研课本,才能理解。
写的有些仓促可能有些遗漏或错误,还请谅解。
-------------------------------------------------------------------------------------


一定要记住寻址方式特别重要,这是我“毕生” 的总结,请笑纳,笑纳。

B56层 发表时间: 05-09-24 16:16

回复: changchang [changchang]   论坛用户   登录
作业里有一道题,EBX=FFFFFFFFH,ECX=00000000H,EAX=0F0F0F0FH,执行CMPXCHG EBX,ECX的结果~~~我的代码:
.386
data segment
n db 5
fact db ?
data ends
code segment
  assume cs:code,ds:data
start: mov ax,data
      mov ds,ax
      mov eax,0f0f0f0fh
      mov ebx,0ffffffffh
      mov ecx,00000000h
      cmpxchg ebx,ecx
      mov ah,4ch
      int 21h
code ends
    end start
编译时该指令所在行提示:Expected: instruction or directive
我把EBX或ECX换成EAX也是一样。
我在一些资料上看到这个是.486以上的指令。我把.386改成.486,发现编译时不认识486这个东东,也给了一个上面的Expected:...
我的CUP是AMD的,会不会是不兼容的问题啊~~~~
各位叔叔阿姨有空帮忙看看~~~先谢了~~~~~~

B57层 发表时间: 05-09-29 21:47

回复: 286 [unique]   版主   登录
1.最新面的.386是表示运行在386保护模式,而不是运行在386的机器上。
2.CMPXCHG 比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX )
3.你的编译器是不是我指定的masm.exe,这个对很多新指令是不是认识的。所以不一定能用。

B58层 发表时间: 05-10-08 17:25

回复: changchang [changchang]   论坛用户   登录
谢谢指导,但我还存在以下的疑问:
1.能不能把前面的386换成486(无法识别这个是不是跟MASM的版本或是CPU有关?)
2.我从前面建议的网站上下了masm和link再把第二个参数改为EAX,但还是同样的错误
3.设计者设计这条指令的意图是什么?即他们设计这条指令主要是用来解决什么问题的?这个指令完全可以用其他的几条指令来实现,是不是这个功能经常用到呢?如果是请举个例子


B59层 发表时间: 05-10-09 02:31

回复: huaxiadayu [huaxiadayu]   论坛用户   登录
写得不错!

B60层 发表时间: 05-12-05 13:51

回复: diri [diri]   论坛用户   登录
好东西,专门注册号进来表达在下的敬佩!

B61层 发表时间: 05-12-07 20:57

回复: FunnyBoy2005 [daviyang35]   论坛用户   登录
写的太好了! 我们小菜一看就懂! 谢谢版主了,
就是更新的有点慢,请多讲点有用的中断

B62层 发表时间: 06-04-04 11:41

回复: sunwu [sunwu]   论坛用户   登录
一点小小建议,可将第零讲 第4小部分 中的极少部分稍改一下,后一个“输入”改为“输出”

4 计算机的组成
  通常都把计算机定义成五部分:运算器、控制器、存储器、输入系统、输出系统。
  为了简单其间,我们如此理解:运算器+控制器=CPU。存储器=内存(暂不包括外存,永不包括CACHE)。输入系统=键盘(不包括鼠标),输入系统=显示器(不包括打印机,绘图仪)。

4 计算机的组成
  通常都把计算机定义成五部分:运算器、控制器、存储器、输入设备、输出设备。
  为了简单其间,我们如此理解:运算器+控制器=CPU。存储器=内存(暂不包括外存,永不包括CACHE)。输入设备=键盘(不包括鼠标),输出设备=显示器(不包括打印机,绘图仪)。




[此贴被 青山绿水(sunwu) 在 07月27日22时19分 编辑过]


[此贴被 青山绿水(sunwu) 在 07月27日22时20分 编辑过]

B63层 发表时间: 06-07-27 22:16

回复: mingren123 [mingren123]   论坛用户   登录
系统共享数据区,这个资料的确是找不到啊

B64层 发表时间: 07-07-02 01:05

回复: hour [hour]   论坛用户   登录
我学过计算机原理,也很感兴趣,老兄讲的有点深刻!@

B65层 发表时间: 07-01-10 16:35

回复: yeniaoer [yeniaoer]   论坛用户   登录
很牛,很好,我也好来学学了

B66层 发表时间: 07-03-17 17:38

回复: _358960822 [_358960822]   论坛用户   登录
如此好贴 怎能不顶!~!~!~!~!~!~

B67层 发表时间: 07-12-29 20:30

回复: ljy1054 [ljy1054]   论坛用户   登录
不错,学习

B68层 发表时间: 11-01-09 13:21

回复: lhyz [lhyz]   论坛用户   登录
顶一下

B69层 发表时间: 12-05-03 15:30

论坛: 编程破解

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号