Board logo

标题: 【Redtek】 个人网志(学习笔记) [打印本页]

作者: redtek     时间: 2006-12-10 00:48    标题: 【Redtek】 个人网志(学习笔记)








学习笔记目录:
==============================================



【80x86 汇编语言学习笔记】 …………………………………………… # 2
              编译的例子 …………………………… # 3
              可见寄存器组 ………………………… # 4
              立即寻址方式 ………………………… # 7
              寄存器寻址方式 ……………………… # 12
              二进制和IP地址的关系 …………… # 18
              位和地址线究其穷破 ………………… # 19
              寻址方式与指令系统 ………………… # 20
              代码练习 - ☺ ………………………… # 21
                   - 256个ASCII码 ………… # 22
              汇编IDE工具 ……………………… # 32
              汇编代码与Debug代码对比 ………… # 33
              可执行文件带入参数分析 …………… # 38
              汇编单行多种颜色写屏 ……………… # 45
              调汇编代码与中文环境 ……………… # 46
              汇编代码学习总结 …………………… # 47
              汇编语言基础知识测试 ……………… # 48
              寄存器汇编指令练习 ………………… # 49
              程序段中指令执行练习 ……………… # 50
              物理地址运算 ………………………… # 51
              寄存器与内存访问 …………………… # 52
              段和偏移量的变换 …………………… # 53
              栈 ……………………………………… # 54
              第一个可执行程序 …………………… # 56
              汇编指令描述方式 …………………… # 57
              循环指令与编译器 …………………… # 60
              汇编指令区别 ………………………… # 63
              安全内存空间 ………………………… # 64
              段前缀的使用 ………………………… # 65
              Debug小技巧 ………………………… # 66
              代码复制自身 ………………………… # 67
              多个段的程序 ………………………… # 68
                     ………………………… # 72
              编写调试跟踪多段程序 ……………… # 73
              灵活定位内存地址 …………………… # 74
              数据处理 ……………………………… # 75
              转移指令与子程序 …………………… # 76
                   ……………………………… # 77
              解决除法溢出问题 …………………… # 78
              子程序与数值显示 …………………… # 81
              结构化数据访问 ……………………… # 84
              标置寄存器 …………………………… # 88
              任意大数字加减 ……………………… # 89
              比较指令 ……………………………… # 90
              计算器使用小窍门 …………………… # 91
              内部中断 ……………………………… # 92
              int指令 …………………………… # 93
              对int、iret和栈的深入理解 ……… # 94
              BIOS和DOS所提供的中断例程 ……… # 95
              端口 …………………………………… # 99
              逻辑位移指令 ………………………… # 102
              外中断与键盘 ………………………… # 105
              直接定址表 …………………………… # 106
              bios键盘输入与磁盘读写 ……… # 107
              exe文件结构 ……………………… # 108
              磁盘操作 ……………………………… # 111
              管道操作 ……………………………… # 112
              字符串操作 …………………………… # 113
              文件操作 ……………………………… # 117

 
 


 
【MS-DOS操作系统学习笔记】 …………………………………………… #  第8页

              引导扇区 ……………………………… # 115
 
 


【Windows32位汇编语言学习笔记】 …………………………………… #  第8页

              前缀详细解释 ………………………… # 114
              建立编程环境 ………………………… # 118
               

 
 
 
【80x86 汇编语言学习笔记】 —— 一句话学习总结


) 找一本认为最难的书学习,更是为了总结最适合自己的学习方法。
  只有读那些看上去 “最难的” 书,才最能挖掘更适合自己的有效学习规律与方法。

) 读看一本很难读的书,才最容易看到自己思想上与能力上的弱点。

) 学与悟的重点放在书上所讲的原理,就等于拥有了创造一万种方法的灵感与能力。

) 无论是解读汇编语言还是解读任何一本书,其实都是在悟读着自己。

) 某个阶段,读到认为掌握了很多,那就是并没有学透多少;读到好象什么都不会了,那是已经读透了部分,要再继续。

) 读适合启发自己思维方式的书,读这样的书才能加倍成长。

) 基础知识就是巨厦的地基,它的深浅有一半注定了最终的计算机水平,它的另一半是对数学的理解。

) 跟着书读是白读。先拉后跟的读是真读。用自己的思考方式一边超前预想一边读,会把见解与书的思想碰撞,这才是读。

) 学习某种编程语言的过程,如果发现只有死记硬背才能继续学下去,那么这个时候就意味着基础没有掌握好,应去补基础。

) 把需要死记硬背才能向下学习的过程,分解成不需要记忆照样可以学下去的过程,那就是掌握它的原理深度来降低记忆强度。

) 读看上去最难的书,总结出适合自己的 “记忆遗忘曲线” 来达到容识更多的知识。

) 世界上少有最难的事。最难读透的书是自己,最难驾御的人是自己,最难逾越的障碍还是自己。
 




 
 

个人网志:
==============================================


【Redtek 学习网志】 http://www.cn-dos.net/forum/viewthread.php?tid=25504

【Redtek 普通网志】 http://blog.sina.com.cn/m/redtek


[ Last edited by redtek on 2007-1-29 at 01:46 PM ]
作者: redtek     时间: 2006-12-10 01:48    标题: 【80x86汇编语言学习笔记】

) 初学汇编,遇破书一本《Intel 80X86/Pentium 汇编语言程序设计(第二版) 》30两银子 - ISBN 7-81045-770-5 。
  奉如无尚珍宝,然作者 “瞎编” ,无奈之下只得瞎学:)

) 初学汇编,今日开始。拿起书狂翻,希望遇到一个 “Hello World!” ,一个没有:)

) 按书顺序,但凡初学者希望遇到一个先能编译,哪怕看一眼运行一下的极短的例子,哪怕在观感上可以“明白”一下,结果没有。

  前96页均为基础知识与寄存器、指令讲解。
  特点:如果你看完多数指令和前100页的知识,也不会编译更不会编。因为书是按照编书次序来编,不是按照帮助你学习的次序来写的。

  代码全是片断,如果没有老师带,完全一个人自学,则那些片断你根本不会编译,就是编译,书上的例子编译器与通不过(现在看来)。
  
  也就是说,如果前96页能全看完,那只不过是超旨记忆,你根本没有机会上机试验,那些全是理论。且和Debug中命令方式不同。

) 总结: 注定了看这本书不人性化作者编的这本书,要走极多的弯路。


) 谁说过:“每年要看一本最难的书……”,于是
  俺坚决:今年看一本最难的书 —— 汇编语言! 明年 ——— 易经,学着画一堆小横线~:)
 
◇ 2006-12-09 12:49:57.17



为这本书平反:初学汇编,当学习了一些汇编基础知识以后,再看这本书就可以看懂了。
       所以,这本书现在看来不是不好,而是适合有汇编基础的人学习或是学校有这门课程并做参数。
       这本书内容较全面,当看完了300多页的汇编基础知识后,发现这本书现在适合读了:(

◇ 22:36 2007-1-17

[ Last edited by redtek on 2007-1-17 at 10:37 PM ]
作者: redtek     时间: 2006-12-10 03:24    标题: 【80x86汇编语言学习笔记】

P12页: 用 IBM PC 汇编语言实现 123+456

书上举例
        CODE  SEGMENT
              ORG       100H
              ASSUME    CS:CODE,DS:CODE
        MAIN  PROC      NEAR
              MOV       AX,A
              ADD       AX,B
              MOV       SUM,AX
              RET
           A  DW        123
           B  DW        456
         SUM  DW        ?
        MAIN  ENDP
        CODE  ENDS
              END       MAIN
) 尽管不懂,没关系先输出试试:)
) 怎么编译呀?要是按书上顺序看,早TMD放弃了!
  翻开书第 P98页《汇编语言上机过程》,照上面例子编译。
  用什么编译?用 MASM5.0编译,编译器在哪儿?在网上找吧……温无边际的找……
  运气好的话一会儿就找到了,运行不好的话编译器有 16位纯DOS上用的,用Win32的,一遍遍试吧,这本书上没有相关内容:)

  我就属于那种运气不好的人,嘿嘿……反复换了4、5个编译不同版本的编译器,自学的步步都是错误:)

  自学也难在这里,深深体会到~:)

  没关系~:)
  现在可以编译了~:P
Invalid keyboard code specified
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.

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

  50980 + 450380 Bytes symbol space free

      0 Warning Errors
      0 Severe  Errors
上面编译完全正确~:)

会看到第一行有一句提示:“Invalid keyboard code specified” ,起初不懂:)
后来反复发现只要切换至代码页437(美国),只要不是936(中国)就可以了,
因为这个编译器是纯DOS上使用的,而我现在 WINDOWS CMD SHELL中编译的。

后来查询Windows自带的CHM帮助,发现如果希望兼容一些平时不能运行的DOS程序,则一个 Forcedos 命令可以支持~:)

如:
[cocd]
C:\Masm50>forcedos /?

FORCEDOS [/D directory] filename [parameters]
/D directory    Specifies the current directory for
                the specified program to use.
filename        Specifies the program to start.
parameters      Specifies parameters to pass to the program.
[/code]

这样,它就不会再提示那行信息了~:)

编译完成:)
Directory of C:\Masm50

2006-12-09  13:39               392 myasm.asm
2006-12-09  14:13               100 MYASM.OBJ
生成 .Obj 文件了,还要再链接成 EXE 文件:)
C:\Masm50>link /?

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

LINK : fatal error L1002: ? : unrecognized option name

C:\Masm50>link myasm.obj

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

Run File [MYASM.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
LINK : warning L4021: no stack segment
链接完成了,Link的使用也在书上P99页~:)
2006-12-09  13:39               392 myasm.asm
2006-12-09  14:18               785 MYASM.EXE
2006-12-09  14:13               100 MYASM.OBJ
这个 myasm.exe 文件生成了~:D
C:\Masm50>forcedos /d %cd% myasm.exe
要这样用Windows兼容的方式运行它,否则系统就提示关于崩溃的内容……
但是,运行完以后屏幕并没有显示任何字符:(

书上的P12页(这可是入门开始啊),上面说了:

程序的运行结果是:
  12F8:010F 43  02

嘿嘿……不能失望,这是在调试状态下你能看到的运行结果,哈哈……
这不是单独运行你所能看到的,这书的作者就是这样的龌龊(这词终于用上了~:K)

但是,如果你要深究其内容与结果怎么办?
那就要用Debug了,Debug在这本书上有一个命令帮助索引(和Debug自带的?帮助一样)。

也就是说,初学汇编的人看这个例子除了一饱眼福之外就是头晕了~:)
我会认为这作者的水平很高,够机器!

◇ 2006-12-09 14:26:38.21


写这样的汇编代码,用记事本或Edit写吗?

有一个叫做《轻松汇编》的编辑工具,语法加亮,自动对齐语句,自动错误检查,自动编译……

使用它来写代码可以少走一些弯路~:)
而且,即然是汇编语言,所以吸烟的时候也应该用火柴点烟才有味道~:D
◇ 2006-12-09 14:54:22.88

[ Last edited by redtek on 2006-12-9 at 05:59 PM ]
作者: redtek     时间: 2006-12-10 04:08    标题: 【80x86汇编语言学习笔记】

附件 1: pic_7.jpg (2006-12-10 04:08, 29.3 K, 下载附件所需积分 1点 ,下载次数: 4)



作者: ccwan     时间: 2006-12-10 05:57
偶来捧场^_^
作者: redtek     时间: 2006-12-10 07:00
多谢ccwan鼓励~:)
作者: redtek     时间: 2006-12-10 07:50    标题: 【80x86汇编语言学习笔记】

(书) P23页 与数据有关的寻址方式:


立即寻址方式
MOV  AL,6                          ; 结果 (AL)=6, 6是立即数
MOV  AX,1234H                  ;  结果(AL)=1234H
) 书上关于立即寻址的方式只有上面这两句指令,不编译一个小代码根本不能直观的理解那些寄存器……

  如果死记硬背,这需要一直抽象的背到第79页才能知道EXE文件的例子。
  且书上没有完整可供编译的代码例子试验,也就是说很有可能当你看完100页的编程的教程,一个程序还未真正编过,这不是在学习。
  
  如果按照这书的顺序,这简直是在要求你类似于学批处理,从低级看到高级例子,但从来不编写一句代码,想必这种学习是浪费生命。



  所以,只能将上面代码尽可能转成Debug调试器里能“看”到的效果,虽然编不出来,但至少能理解。

  下面是使用 Debug 调试器测试书上(上面)两句代码的过程,彩色数字是观察到的寄存器直观的变化。

  Quote:
-R
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0100   NV UP EI PL NZ NA PO NC
13BF:0100 0000          ADD     [BX+SI],AL                         DS:0000=CD
-A
13BF:0100 MOV AL,6
13BF:0102 MOV AX,1234
13BF:0105
-T=100

AX=0006  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0102   NV UP EI PL NZ NA PO NC
13BF:0102 B83412       MOV     AX,1234
-T

AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0105   NV UP EI PL NZ NA PO NC
13BF:0105 0000          ADD     [BX+SI],AL                         DS:0000=CD

Debug在这种破不得已的状态下真是太有用了~:)

[ Last edited by redtek on 2006-12-9 at 08:27 PM ]
作者: vkill     时间: 2006-12-10 07:51
呵呵~sina 上的图片挺好的
作者: redtek     时间: 2006-12-10 07:54
平时喜欢摄影,没事儿就乱拍~:)
作者: lxmxn     时间: 2006-12-10 08:36

  哈哈,偶也来捧场啦~

  想不到 Redtek 兄这么快就开网志了~SINA上面还有几个批处理的实例供偶学习学习的~~

  感觉这汇编挺难的,没信心了…… o_O

作者: redtek     时间: 2006-12-10 08:46
哈哈……
Sina的网志写了有一段时间了,多半都是摄影内容~:)
俺觉得这里放代码学习内容为主的网志最合适不过:D

汇编太难学(感觉按书上次序学简直需要极高的耐心),而且全是抽象的一两行的汇编片断(这片断很多都不能在Debug里直接编)。
编译器要求的代码表示和Debug里的直译表示不一样,书上又只讲理论,不讲都是干什么用的,更抽象了……

不过,难就难吧,哈哈……
一天啃一两句,早晚能啃下来~:)
作者: redtek     时间: 2006-12-10 09:46    标题: 【80x86汇编语言学习笔记】

寄存器寻址方式

(书) P24页
MOV   BX,AX                ; 执行结果(BX)=(AX)
访问普通变量时,默认是DS段寄存器,但也可以显式的指定为ES、SS、CS、GS、FS。
可以使用段超越前缀显式地指定段寄存器。


debug学习过程:

  Quote:
-A
13BF:0100 MOV AX,5678
13BF:0103 MOV BX,AX
13BF:0105
-T=100

AX=5678  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0103   NV UP EI PL NZ NA PO NC
13BF:0103 89C3          MOV     BX,AX
-T

AX=5678  BX=5678  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0105   NV UP EI PL NZ NA PO NC


作者: pengfei     时间: 2006-12-10 11:14
Redtek兄真是性情中人, 网志搞得有声有吗.  呵呵~ 那个烤黄粉虫的好, 有营养...

自学的苦与乐只有自己才能品尝到. 加油~~~!
作者: redtek     时间: 2006-12-10 12:10
多谢pengfei兄鼓励~:)
作者: electronixtar     时间: 2006-12-11 01:56
看到牛XXXXX的汇编了,不懂。顶一个
作者: redtek     时间: 2006-12-11 07:27
electronixtar兄来杯茶~~~
照着书看简直是天书,无奈还得啃下去,哈哈……
作者: redtek     时间: 2006-12-11 22:26    标题: 一个可能存在危险的汇编工具

除了《轻松汇编》的汇编语言编辑工具,又看到了一个名为《至死汇编》的工具。
“这是一款全中文环境的傻瓜型DOS汇编语言辅助开发工具”……它是这样描绘其功能的。

然而,在找到数个可以下载《至死汇编》的网站后,却看到了一系列神奇的事件:
            卑鄙的[编程傻瓜- 到死汇编]

天空软件站等很多网站提供了这个软件的下载,尽管软件简介天花乱坠,但用过的人就知道,请神容易送神难!不仅无法卸载,更是发现了很多用户已经发生硬盘无法访问、分区表等被删除de个案。所以,我万分谨慎的提醒大家,千万不要随便安装这个软件,除非你真的有钱,并且不在乎。

亲生测试有屎以来最无耻的软件"编程傻瓜 -
到死汇编"

软件有个许可证9.txt

竟然这样写:
4、您必须按照软件说明书中的要求及软件给您的交费提示,为您使用该软件所花的费用,在指定的日期内(此日期以您用来使用此软件的电脑上的日期为准),按指定的途径、方式来交费。
5、您不得采用任何手段来逃避交费;否则,您愿意单独全部承担由此引起的一切后果和责任。
6. 您愿意将此电脑(即:您用来执行此软件的电脑)作为财产抵押,抵押给北京军民软件园;当您违反此协议的任何条款时,则此电脑及其全部数据的财产所有权归北京军民软件园所有,北京军民软件园可以在任何时候、任何地点、以任意方式处置此电脑及其全部数据。

……总之,大至的被评论的事件就是:你装了这个软件,如果不交注册费,你的机器你的分区你就等着数据全丢吧,引导也不管用了,全被重写了。
以上贴子内容来源:
http://www.blogcn.com.cn/user1/525/archives/2005/11376.shtml

虽不能确定以上评论是真实事件还是别有用心的特殊商业竞争手段……
但,我是不敢装它了!这数据可丢不起:)

为什么这工具软件的名字居然叫《到死汇编》,哈哈……
装过的人气得快要死了吧?!

(另:以上内容只介绍了网上对《到死汇编》的部分评价,不对其介绍的真实性负责)
作者: redtek     时间: 2006-12-11 23:12    标题: 【80x86汇编语言学习笔记】

领悟二进制和IP地址的关系
字:   1个字16位,其位编号为 b15~~b0。
     1个字占用2个存府单元。

双字:  1个双字32位,其位编号为 b31~~b0。
     1个双字占用4个存储单元。

四字:  1个四字64位,其位编号为 b63~~b0。
     1个四字占用8个存储单元。
经常见到的存取信息的基本单位,英文为:Byte,就是“字节”。
一个字节由8位二进制数组成,其位编号自左至右为 b7~~b0。
(即,1个字节占用1个存储单元)

计算机中存储信息的基本单位是1个二进制位,简称:位,英文 bit,可用小写字母 b 表示,一位可存储一位二进制数。

(以上内容约为书上介绍基本概念)
(这概念有用吗?在汇编里非常有用,几乎全是这样的数据类型)
(在计算机“看来”,一切数据都是二进制信息)
(那些常用的 Windows 记事本 里看到的文本或是中文信息,那是为了给我们方便阅读,能够看得懂)
(而在计算机中,我们常称之为“文本文件”的内容,其实在计算机里都是二进制信息)
(只不过调出来为了让我们看得懂,才通过一系列“转化”和一系统“概念”,才从二进制“翻译”成我们看得明白的内容)


32位有多长?经常听到有人说:“IP地址是32位的二进制地址”。
不过,上面的定义只是网络书上或是经常有人提及的一个概念:……32位的二进制地址。

那么这个概念能不能给它“实例”化呢?

(注:以下只是个人分析,不保证个人理解和个人所说的是正确的)

例: 127.0.0.1

二进制表示这个地址: (要用 Windows Calc 计算器了,手工计算要疯掉!)
1111111.0.0.1    (二进制表示 127.0.0.1 IP地址)
但,它并不是32位的,所以左边要被位,让它为4个8位为一组。
就象是: 128,而我给128的左边加0,变成了: 00000128,那么它也是128 ~:)
01111111.00000000.00000000.00000001  (这才是32位的二进制地址的初步表示)
那么真实的32位IP地址:127.0.0.1 在计算机看来应该如何表示呢?
01111111000000000000000000000001
是上面这个样子,这才是真正的32位的二进制IP地址的书写。
在计算机看来,一切都是二进制信息,所以那个分隔的“点”就没有了~:)
为什么IP地址中间要有点儿?通过以上分析得出结论:那是为了让我们能够看懂和能够方便记忆,所以加上去的--人性化。

计算机可看不懂,它就认纯粹的二进制信息~:)

对了,现在想起了有的人(看操作象是高手的方式),有的人为了隐藏IP地址,它将IP地址“缩为”一串儿看上去很奇怪的数字。

这是为什么?

如:你会看到一些  http://2130706433
  或是 Ping  2130706433
  或是 Ftp …………

那就让我来揭穿,或者说从底层二进制来弄明白这个数字、这种表示方法到底是什么:)

下面是我们刚才计算出来的32位的二进制的IP地址表示:
01111111000000000000000000000001
这么长,哈哈……(该不会有人问那么"Redtek"我的名字是不是也可以用二进制表示?当然能,只是到时候我都不认识自己了:)

好,最简单的办法就是将上面的32位二进制地址计算成10进制就行了,让windows的计算器算吧~:)

打开 Windows 的计算器,选择“科学型”计算方式,在二进制那里COPY上面地址,然后点“十进制”就转出来了:)

转成十进制的地址信息如下:
2130706433
上面就是10进制的IP地址信息了,我们怎么验证它呢?
Ping 它啊~~
C:\TEMP\debug>ping 2130706433

Pinging 127.0.0.1 with 32 bytes of data:

Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128

Ping statistics for 127.0.0.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% los
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
看到了,只要一Ping 它,Ping 就自动转化为我们看得懂的地址信息了。
所以, Http://2130706433  当然也能~:)
而且,当你这样操作了,你就会看到浏览器的提示栏里显示“……127.0.0.1……” ~:)

等等……你想到了什么?
这是我在写另一个网志的时候想到的……“喂……你们的网址是多秒?” ……太长了……我记不住,英文字母发音也不准……
如果你将你公司的HTTP首地址写成上面的10进制地址,是不是读给客户听更方便更好记忆呢?
是不是你用手机或是移动设备浏览被你“编码”10进制的地址信息输入更方便呢?谁让手机输入数字比输入字母快~:)
(注:无独立IP的主机无效)


以上就是个人总结的关于二进制与计算机中我们常说的IP地址的关系:)


10:04 2006-12-11


另:既然我们上面聊到了 Ping ,那么 Ping 还可以干什么呢?
  Ping 可以看懂10进制地址信息,那么我们让 Ping 来做翻译吧~:)

  我们知道: 字母 A 的16进制是 41 ,它的10进制是 65
  set /a 0x41 也可以帮我们计算出 0X41 这个16进制的10进制是 65  

  所以,这回我们急了!!!我们要PING 0x41
C:\TEMP\debug>ping 0.0.0.0x41

Pinging 0.0.0.65 with 32 bytes of data:
Ping 帮我们把 0x41 转成了10进制数: 65,哈哈……
Ping 也可以当计算器~:)

上面的原理还是来自于二进制信息与什么是32位的二进制IP地址的我们刚才的讨论~:)

但Redtek本人不表示上面所说就一定是正确的,很可能等汇编语言完全学完以后上面的个人认为又是一种错误~:)


(另:为什么IP地址如: 127.0.0.1  这个子网的范围为什么是:0~~255呢?)
(  这是谁规定的? 为什么要这样规定?  )
(  好象没人告诉我,好象就是指定这个范围 )

好,我们把这个弄清楚了~:)

IP地址是由32位的二进制地址组成。
由上面推断证明,它是每8位一组,一共四组,每组用 “点” 来隔开,为了让我们看明白:)
01111111.00000000.00000000.00000001
上面就是四组,每组8位二进制表示的IP地址。
8个位的二进制位等于一个字节的长度,
8个位的二进制数可以最大表示(10进制无符号数):  0~~255
所以,简单认为如果有一个10进制是256,那么它就超过了8位二进制所容纳的范围了(无符号数--即没有正负之分)。

255的16进制是FF。
当我们在8位寄存器中,用Debug做如下实验:
C:\TEMP\debug>debug
-a
0AF5:0100 MOV AL,100
                   ^ Error
0AF5:0100 MOV AL,FF
把 100H (10进制的256)送入 8位的AL寄存器,结果溢出。
把   FFH (10进制的255)送入 8位的AL寄存器,刚好:)

FFH (10进制的255)转成二进制是: 11111111 (正好占8个二进制位)
100H(10进制的256)转成二进制是:100000000 (占9个二进制位,8位寄存器可放不下)

所以,取值这是样规定了从0~~255的范围刚好合适。

那么 255.255.255.255  是多大呢?
二进制表示: 11111111111111111111111111111111
它的十进制: 4294967295
  相当于: 42亿多台主机……(不过这是理论值)

那么,经常遇到什么什么溢出也是这个原因~:)

[ Last edited by redtek on 2006-12-11 at 11:00 AM ]
作者: redtek     时间: 2006-12-12 02:13    标题: 【80x86汇编语言学习笔记】

位和地址线究其穷破

二进制位、n位地址线……这些都是什么?它们有没有用?

书上只告诉我这样的原话:“……8088……,有20位地址线,可以寻址1MB的内存……”

(好,我们今天非得把它弄明白了不可)
(书上一带而过,并没有说为什么20位地址线就可以寻址1MB内存,它凭什么能寻得,又凭什么寻不到1MB以上内存?)
(这20位地址线最大寻址为什么是1MB?怎么计算出来的?)
(好,我们从现在把它完全弄明白了,一直推到它的本质上~:)

(……休息,休息一会儿)


第17楼从位一直推到那个IP地址,又一直推到那个IP地址的最大表示范围。

所以,我们知道二进制表示8个位的最大数(8位寄存器也可以不溢出的情况下装入),那么这个数就是十进制的255。
转成16进制是 FF (还记得修改游戏血值为什么是FF了吧?代表最大)。
转成二进制就是 11111111 (一共有8个1,哈哈)

所以,那么20位地址如何表示呢?当然是20个1啦~:)
11111111111111111111

那么这20个1(就是二进制的20位表示)的10进制是多大呢? 1048575
(为什么要表示成10进制呢?二进制容易看晕了,不方便用windows计算器计算)

好,20位的10进制得出了是:1048575
(书上说20位地址线最大寻址为1MB内存)

好,我们再看1MB的内存计算方法:

1024个字节=1KB
1024KB=1MB

所以,1024KB是1MB,那么1MB就是1024*1024=1048576个字节

1MB是1048576个字节,那么为什么刚才计算20位最大数表示的10进制是1048575呢?它两个数可是相差1啊!

再看一下内存,内存的启址地址是从0开始的,一直到20位地址可寻范围的1048575,当然有1048576个地址表示。

而我们1024*1024所得到的内存1MB的计数,可是从1到1048576的,它们起始数不一样,当然相差1。

所以,20位地址线最大可访问内存为1MB。

[ Last edited by redtek on 2006-12-15 at 08:23 AM ]
作者: redtek     时间: 2006-12-12 23:25    标题: 【80x86汇编语言学习笔记】

IBM PC 机的寻址方式与指令系统


与数据有关的寻址方式包括:
 
1) 立即寻址方式
2) 寄存器寻址方式
3) 直接寻址方式
4) 寄存器间接寻址方式
5) 寄存器相对寻址方式
6) 基址变址寻址方式
7) 相对基址变址寻址方式
8) 比例变址寻址方式


“ 一条指令通常由操作码域和操作数域两部分组成。 ”
“ 寻址方式指的就是指令按什么方式寻找到操作数地址或操作数。 ”


EA=有效地址。
   在IBM PC中就是操作数地址的偏移量部分。(简称EA)




“ EAX、AX、AL(通常被分为32位、16位、8位累加器),它们在乘除、十进制运算、输入输出指令中有专门用途。 ”
“ 在实模式中BX、BP、SI、DI可以作为间接寻址的寄存器,用以寻址64KB以内的内存单元。 ”





1) 理解立即寻址方式

  Quote:
-A
0AF7:0100 MOV AL,6            ; 结果 (AL) = 6 , 6是立即数
0AF7:0102 MOV AX,1234      ; 结果  (AX) = 1234H , 1234H是立即数
0AF7:0105
-T=100

AX=0006  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0102   NV UP EI PL NZ NA PO NC
0AF7:0102 B83412        MOV     AX,1234
-T

AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0105   NV UP EI PL NZ NA PO NC

2) 理解寄存器寻址方式

例: MOV  BX,AX            ;  执行结果(BX)=(AX)
   MOV  DI,5678H       ;  执行结果(DI)=5678H

“ 仿问普通变量时,默认选用DS段寄存器,后也可以显式地指定为ES、SS、CS(在386以上还可以为GS、FS)。”

段超越前缀的格式为: 段寄存器名:
段超越前缀的功能是明确指出本条指令所要寻址的内存单元在哪个段。

例: MOV  BX,ES:MEM

“  该指令源操作数的有效地址是用变量名表示的MEM,前边使用了段超越前缀“ES:”,明确表示使用ES所指向的附加数据段中变量,
而不是默认的DS所指向的数据段。 ”

由于汇编格式不会,下面使用Debug理解寄存器寻址方式:

  Quote:
-R
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0100   NV UP EI PL NZ NA PO NC
0AF7:0100 B006          MOV     AL,06
-A
0AF7:0100 MOV AX,1234
0AF7:0103 MOV BX,AX
0AF7:0105
-T=100 2


(把1234H放入AX16位寄存器中)
(将AX寄存中的值放入BX寄存器中)


AX=1234  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0103   NV UP EI PL NZ NA PO NC
0AF7:0103 89C3          MOV     BX,AX

AX=1234  BX=1234  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0105   NV UP EI PL NZ NA PO NC

3) 理解直接寻址方式

“ 操作数的有效地址直接包含在指令中的寻址方式。 ”
“ 有效地址存放在代码段的指令操作码之后,但操作数本身在存储器中,所以必须先求出操作数的物理地址。 ”


例: MOV  AL,[78H]

Debug理解上面代码:

  Quote:
-R
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0100   NV UP EI PL NZ NA PO NC
0AF7:0100 BF7856        MOV     DI,5678
-E DS:1500 "1234"

(将字符串 “1234” 写入DS段偏移量1500H处)

-A
0AF7:0100 MOV AL,[1500]
0AF7:0103
-T=100

(MOV AL,[1500] 是将DS段偏移量1500H处的8位字符(变量)送入AL8位寄存器中)



AX=0031  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0103   NV UP EI PL NZ NA PO NC
0AF7:0103 89C3          MOV     BX,AX


(显示DS段偏移量1500H处数据,其中的“31”为数字1的16进制ASCII码)

-D 1500
0AF7:1500  31 32 33 34 07 58 5F C3-33 C0 0B C9 74 4F 1E 06   1234.X_.3...tO..
0AF7:1510  1F B4 40 8B D7 83 F9 01-75 28 CD 21 1F 50 26 8A   ..@.....u(.!.P&.
0AF7:1520  05 E8 5A 00 58 1E 06 1F-73 06 F8 B4 40 42 CD 21   ..Z.X...s...@B.!
0AF7:1530  9C 26 80 3D 1A 75 02 8B-C1 EB 01 CF 0E E8 FB FF   .&.=.u..........
0AF7:1540  EB 1A 55 51 E8 A6 FF 8B-E9 59 CD 21 73 02 EB 0B   ..UQ.....Y.!s...
0AF7:1550  3B C8 74 07 3B E8 F8 75-02 8B C1 5D 1F C3 B8 27   ;.t.;..u...]...'
0AF7:1560  00 BB 04 00 B5 01 C3 80-FE FF 74 11 F6 C6 80 75   ..........t....u
0AF7:1570  0C 1E 07 8D 3E 67 92 B9-02 00 E8 00 FF C3 06 57   ....>g.........W

段超越前缀Debug表示如下:(汇编表示: MOV  AL,ES:[78H] )

  Quote:
-E ES:78 12
-A
0AF7:0100 ES:MOV AL,[78]
0AF7:0104
-T=100

AX=0012  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0104   NV UP EI PL NZ NA PO NC
0AF7:0104 20750A        AND     [DI+0A],DH                         DS:000A=4F

(78H处内容:)

-D 78
0AF7:0070                          12 00 00 00 00 00 00 00           ........

4) 理解寄存器间接寻址方式

“ 若指令中使用的是BX、SI、DI、EAX、EBX、ECX、EDX、ESI、EDI,则缺省情况操作数在数据段。 ”

寄存器间寻址方式[BX]表示BX寄存器中是有效地址而不是操作数。
这也是寄存器间寻址方式与寄存器寻址方式在汇编格式上的区别。

例: MOV  AL,[BX]
   若(DS)=3000H,(BX)=78H,(30078H)=12H
   则物理地址=10H*(DS)+(BX)=30078H
   该指令结果是(AL)=12H

Debug 理解:

  Quote:
-E DS:78 12
-A
0AF7:0100 MOV BX,78
0AF7:0103 MOV AL,[BX]
0AF7:0105
-T=100

(将地址DS:78H送入BX寄存器中)
(取BX寄存器中的地址值,送入AL寄存器中)

AX=0000  BX=0078  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0103   NV UP EI PL NZ NA PO NC
0AF7:0103 8A07          MOV     AL,[BX]                            DS:0078=12
-T

AX=0012  BX=0078  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0105   NV UP EI PL NZ NA PO NC

5) 理解寄存器相对寻址方式


“ 操作数的有效地址是一个基址(BX,BP)或变址寄存器(SI、DI)的内容和指令中给出的一个位移量(disp)之和。”

“ 对于386以上CPU,这种寻址方式允许使用任何32位通用寄存器。 ”
“ 位移量可以是1个字节、1个字、1个双字(386以上)的带符号数。 ”

“ 若指令中寄存器相对寻址方式使用BP、EBP、ESP,则默认与SS段寄存器配合。 ”
“ 使用其他通用寄存器,则默认与DS段寄存器配合,允许使用段超越前缀。”


EA=(基址<或变址>寄存器)+disp
EA=(32位通用寄存器)+disp   (386以上)


Debug理解如下:

  Quote:
-E 78 12

(将12H填入DS段偏移量为78H地址处)

-A
0AF7:0100 MOV BX,70
0AF7:0103 MOV AL,[BX+8]
0AF7:0106

(将70H地址送入BX寄存器中)
(取BX寄存器中地址并加位移量8,取这个地址中的值,送入AL寄存器中)

-T=100

AX=0000  BX=0070  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0103   NV UP EI PL NZ NA PO NC
0AF7:0103 8A4708        MOV     AL,[BX+08]                         DS:0078=12
-T

AX=0012  BX=0070  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0106   NV UP EI PL NZ NA PO NC

6) 理解基址变址寻址方式

“ 操作数的有效地址是一个基址寄存器(BX,BP)和一个变址寄存器(SI、DI)的内容之和。 ”

EA=(基址寄存器)+(变址寄存器)

例: MOV  AL,[BX][SI]
   执行结果: (DS:[BX+SI]) ---> AL

“ 使用这种寻址方式可以访问一维数组,其中BX存放数组起始地址的偏移量,SI存放数组元素的下标乘以元素的长度,下标从0计数。”


Debug理解基址变址寻址方式:

  Quote:
-E DS:78 12
-A
0AF7:0100 MOV SI,8
0AF7:0103 MOV BX,70
0AF7:0106 MOV AL,[BX][SI]
0AF7:0108
-T=100

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0008  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0103   NV UP EI PL NZ NA PO NC
0AF7:0103 BB7000        MOV     BX,0070
-T

AX=0000  BX=0070  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0008  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0106   NV UP EI PL NZ NA PO NC
0AF7:0106 8A00          MOV     AL,[BX+SI]                         DS:0078=12
-T

AX=0012  BX=0070  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0008  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0108   NV UP EI PL NZ NA PO NC

7) 理解相对基址变址寻址方式


“ 操作数的有效地址是一个基址(BX、BP)和一个变址寄存器(SI、DI)的内容和指令中给定的一个位移量(disp)之和。”


EA=(基址寄存器)+(变址寄存器)+disp


例: MOV  AL,ARY[BX][SI]
   该指令可以表示为:MOV  AL,ARY[BX+SI] 
            MOV  AL,[BX+SI+ARY] ---> AL


使用Debug理解例子:

  Quote:
-E DS:78 12

(将12H填入DS段偏移量78H)

-A
0AF7:0100 MOV BX,70
0AF7:0103 MOV SI,4
0AF7:0106 MOV AL,4[BX+SI]
0AF7:0109
-T=100

(将地址70H送入BX寄存器)
(将地址4H送入SI变址寄存器)
(求BX寄存器内地址+SI变址寄存器内地址+位移量4H,它们相加总和地址所在的变量取出,送入AL寄存器)


AX=0000  BX=0070  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0103   NV UP EI PL NZ NA PO NC
0AF7:0103 BE0400        MOV     SI,0004
-T

AX=0000  BX=0070  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0004  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0106   NV UP EI PL NZ NA PO NC
0AF7:0106 8A4004        MOV     AL,[BX+SI+04]                      DS:0078=12
-T

AX=0012  BX=0070  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0004  DI=0000
DS=0AF7  ES=0AF7  SS=0AF7  CS=0AF7  IP=0109   NV UP EI PL NZ NA PO NC

使用上面这种寻址方式可以访问形如 ARY[3][2]的二维数组,下标从0开始计数。
假设数组在内存中以行的形式存放(即先存放第一行的所有元素,然后依次存第二行……),其中ARY为数组超始地址的偏移量。
基址寄存器(例BX)为某行首与数组起始地址的距离(即BX=行下标*一行占用的字节数)。
变址寄存器(例SI)为某列与所在行首的距离(即SI=列下标*元素长度),若保持BX不变而SI改变,则访问同一行所有元素。
若保持SI不变而BX改变,则可以访问同一列的所有元素。





8) 理解比例变址寻址方式

“ 这种寻址方式是80386以上的微处理器才提供的。 ”

EA=(基址寄存器)+(变址寄存器)*比例因子+disp


(最后一种相当于第7种寻址方式的扩充,关于模拟数组的方式非常有用)
(但,如果按书 MOV EAX,ARY[EBX][ESI] 就这一句话举例一带而过的讲义方式,非常怀疑这是在简单抽象介绍还是在一步一步教学!)
(所以,如果不模拟并做出一个数组的较完整的操作代码出来,想必是白学了,不可能得心应手的使用和思维再扩展它的应用)

(暂时放在这里,等会汇完整的数组操作代码时再回过来看)

[ Last edited by redtek on 2006-12-12 at 05:40 PM ]
作者: redtek     时间: 2006-12-13 07:02    标题: 【80x86汇编语言学习笔记】

(热身练习~:)


显示一个字符笑脸儿:  ☺

  Quote:
-A
0AF7:0100 MOV DL,1
0AF7:0102 MOV AH,2       ; 调用DOS 21中断2号功能,显示DL寄存器中字符
0AF7:0104 INT 21
0AF7:0106 INT 20
0AF7:0108
-G

Program terminated normally
-U 100 106
0AF7:0100 B201          MOV     DL,01
0AF7:0102 B402          MOV     AH,02
0AF7:0104 CD21          INT     21
0AF7:0106 CD20          INT     20

如果没有这个热身小练习估计再看下去就疯了~:)

(上面小代码也可以在不生成临时文件的情况下自动“注入”Debug并自动生成文件)
echo exit|%ComSpec% /k prompt a$_mov dl,1$_mov ah,2$_int 21$_int 20$_$_rcx$_8$_n p.com$_w$_q$_|debug
上面代码是一行,执行后会在当前目录下生成一个名为 P.COM 的文件,一执行会显示一个笑脸儿~:)

(上面指令无论是 Windows CMD Shell 还是 MS-DOS 纯DOS环境均可以正常执行)

[ Last edited by redtek on 2006-12-12 at 06:19 PM ]
作者: redtek     时间: 2006-12-15 05:59    标题: 【80x86汇编语言学习笔记】

输出 256 个ASCII码~:)

  Quote:
C:\TEMP\debug>DEBUG
-A
0AF5:0100 MOV CX,0100
0AF5:0103 MOV DL,00
0AF5:0105 MOV AH,06
0AF5:0107 INT 21
0AF5:0109 INC DL
0AF5:010B LOOP 0105
0AF5:010D INT 20
0AF5:010F
-G
☺☻♥♦♣○
♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]
^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡
«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²


Loop   为循环指令,其循计数放在CX寄存器中。
06h    DOS 21中断06H号功能:为直接控置台读/写,要输出的ASCII字符送入DL寄存器中
inc    递增指令,指定寄存器+1每次。


[ Last edited by redtek on 2006-12-14 at 05:11 PM ]
作者: lxmxn     时间: 2006-12-15 08:29

  哈哈,就再这里跟着兄学习汇编了。比书上详细、好理解的多。呵呵~~

  Quote:
1024个字节=1KB
1024KB=1MB

(1MB就是1024个字节)

  19楼中,这里笔误了吧?

作者: namejm     时间: 2006-12-15 09:20
  哈哈,无聊如汇编都让 redtek 兄搞得让人兴味盎然,过瘾过瘾。以后论坛里如果要开讲座的话,老兄一定是在台上先泡杯花茶然后再唾沫横飞开讲的那位,哈哈。
作者: lxmxn     时间: 2006-12-15 10:30

  不幸找了一个汇编的网站,去看看?http://www.aogosoft.com/

作者: redtek     时间: 2006-12-15 21:25
多谢lxmxn兄,19楼的错误去掉了~:)))
http://www.aogosoft.com/ 这个汇编网站不错,收藏:)
作者: redtek     时间: 2006-12-15 21:38
有了namejm兄的鼓励,俺心中顿时暖流四起、真气充盈,突然间年轻了20年~:)
什么时候论坛开讲座,真盼着能一桌子酒……边喝边听namejm兄演讲~:)~
作者: GOTOmsdos     时间: 2006-12-16 00:26
过来纯支持一下,偶只会操作MBR的汇编
汇编是编程语言的《易经》
作者: redtek     时间: 2006-12-16 02:06
“汇编是编程语言的《易经》” GOTOmsdos兄这句话说得真是贴切至极!!!欣赏!!!
作者: lxmxn     时间: 2006-12-16 02:27


  Quote:
echo exit|%ComSpec% /k prompt a$_mov dl,1$_mov ah,2$_int 21$_int 20$_$_rcx$_8$_n p.com$_w$_q$_|debug

  我生成了这个P.COM,但是不知道专门看见这个“笑脸”。运行一闪而过,根本看不见,请教兄一下~

作者: redtek     时间: 2006-12-16 02:46
在那个CMD Shell 黑窗口里运行 P.com ~:)
作者: redtek     时间: 2006-12-17 00:09    标题: 【80x86汇编语言学习笔记】

微软 MASM 6.11 版自带开发IDE工具,可以很方便的编译ASM文件。书上的代码也可以正常编译了~:)
且 MASM 6.11 为微软安装版,故工具很全。而其它网络下载的不同MASM版均为“简版”,照不同的书编译不同的版本非常麻烦。

在此之前,书上的部分代码有的可以在 MASM 5.X版编译,有的出错。
而出错的代码部分可以在 MASM 6.11 版编译,这些 “不兼容” 的现象给自学汇编带来了很多障碍。
附件 1: 1.GIF (2006-12-17 00:09, 46.46 K, 下载附件所需积分 1点 ,下载次数: 1)



作者: redtek     时间: 2006-12-17 01:54    标题: 【80x86汇编语言学习笔记】

下面代码是教程示例代码, Masm 6.11编译通过,Masm 5.0编译不通过。
起初在编译电子教程上示例代码时,怎么都编译不通过。由于无汇编编译经验,找不出来它的问题。

后想起了批处理中常使用中文全角空格做文本对齐技巧,故在“替换”中找到并替换掉了那些为HTML对齐方便而被作者输入的中文空格。

去除了所有中文空格以后,Masm611编译通过。
                    
        CSEG  SEGMENT   'CODE'
              ORG       100H
      START:  LEA       DX, MSG
              MOV       AH, 09H
              INT       21H
              MOV       AX, 4C00H
              INT       21H
         MSG  DB        "HELLO WORLD!$"
        CSEG  ENDS
              END       START
对以上汇编代码的语句不是很明白,所以用Debug实现上面代码的同理功能~:)
C:\MASM611\Redtek>debug
-A
0B05:0100 MOV DX,0100         ;  暂时不知道要输出内容Hello所在地址,先瞎添一个。
0B05:0103 MOV AH,09
0B05:0105 INT 21
0B05:0107 INT 20
0B05:0109 DB "Hello!$"        ;  这回知道了"Hello!"在0109,一会再将0100处改回来。
0B05:0110

-A 0100
0B05:0100 MOV DX,0109         ;  修改0100处代码,将存有"Hello!"字串地址送入DX中。
0B05:0103
-RCX
CX 0000
:10                           ;  编完的.com可执行文件的长度 16 个字节~:)
-N HELLO.COM
-W
Writing 00010 bytes
-Q
上面Debug编完的代码执行后,输出如下:
C:\MASM611\Redtek>hello.com
Hello!
由上面可见Debug做简单短小代码最直接、最方便!~:)
书上以及教程上并没有给出一个最简单的汇编编译器可以编译的.ASM代码和与Debug实现同样功能的对比代码。

而书上所举示例均为片断,而上面所举汇编.asm格式代码也是在第十章(也就是说你几乎都快学完了所有汇编课程)才能见到如何编译。
所以,根本不可能直观了解并一边编译一边揣摩那些指令是如何实现的。

(而且,巧合还是奇怪的事情,居然几本教材上几乎都是编译以及如何编译和完整的一个小代码全在整本书的快结束部分,全互相抄?)

所以,走了一个多星期的弯路将这相同的功能(除那个MOV  AX, 4C00H不实现)的两个不同实现方法总结贴上。

只有这样,一边学一边试着编译和Debug实现,才可以真正掌握一些东西,并少走弯路。


(感谢Lxmxn给我的汇编教学网站地址)

[ Last edited by redtek on 2006-12-16 at 01:09 PM ]
作者: lxmxn     时间: 2006-12-17 16:29


  Quote:
Originally posted by redtek at 2006-12-15 13:46:
在那个CMD Shell 黑窗口里运行 P.com ~:)


  呵呵,这回行了。可能上次字体颜色比较浅,没有看清楚吧。

作者: redtek     时间: 2006-12-17 22:05
~:)
作者: ccwan     时间: 2006-12-17 23:05
redtek兄的讲座精彩极了!欣赏~~

  Quote:
Originally posted by GOTOmsdos at 2006-12-16 00:26:
过来纯支持一下,偶只会操作MBR的汇编
汇编是编程语言的《易经》

偶正在学习“RMB”的汇编^_^
作者: redtek     时间: 2006-12-17 23:11
哈哈……今天没出去玩?

我这日志纯粹是为了激励自己再难也必须学下去,能一步一步的看到学习的过程~:)

[ Last edited by redtek on 2006-12-17 at 10:22 AM ]
作者: redtek     时间: 2006-12-17 23:35    标题: 【80x86汇编语言学习笔记】

[可执行文件带入参数分析]



一直很好奇,一个可执行文件或是一个可运行的文件,它是如何获得我所输入的参数呢?
如: DIR /? ,这个 “/?” 就是我运行Dir时所输入的参数:)


下面的过程是简单用Debug装入了一个可执行文件,后面是我带的参数:“LINE3  COL4”,是两个参数:)

当 COL.COM 程序一启动以后,当前环境变量以及运行时所带的参数均被装入内存并存在这个 .com 文件的 256B之前的内存段内。
.com 文件段是连续存放的,不超过64KB。


(下面是“截屏字符拷贝,有些错位)

  Quote:
C:\TEMP\debug>DEBUG COL.COM LINE3 COL4
-R
AX=0000  BX=0000  CX=000C  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B39  CS=0B39  IP=0100   NV UP EI PL NZ NA PO NC
0B39:0100 B402          MOV     AH,02
-S 0000 010C "LINE3"
0B39:005D
0B39:0082
-D 005D
0B39:0050                                                            4C 49 4E                  LIN
0B39:0060  45 33 20 20 20 20 20 20-00 00 00 00 00 43 4F 4C   E3      .....COL
0B39:0070  34 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00   4       ........
0B39:0080  0B 20 4C 49 4E 45 33 20-43 4F 4C 34 0D 45 33 20   . LINE3 COL4.E3
0B39:0090  43 4F 4C 34 0D 50 33 33-30 20 54 33 0D 74 2E 65   COL4.P330 T3.t.e
0B39:00A0  78 65 0D 00 00 00 00 00-00 00 00 00 00 00 00 00   xe..............
0B39:00B0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0B39:00C0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0B39:00D0  00 00 00 00 00 00 00 00-00 00 00 00 00            .............
-Q

[ Last edited by redtek on 2006-12-17 at 03:42 PM ]
作者: ccwan     时间: 2006-12-17 23:36
我很佩服兄的热情和毅力,我若如兄般刻苦,也不至于这么菜了。
真不懂你怎么有如此大的精力,涉猎这么广。
作者: redtek     时间: 2006-12-17 23:38
兄过讲了,我光学这汇编都满脑袋飘星星了,哈哈……
作者: ccwan     时间: 2006-12-17 23:46
临走时劝兄一句话,鸡翅薯条之类垃圾食品少吃点,注意身体。
作者: 9527     时间: 2006-12-18 04:30
我也来捧个场儿,兄是个摄影爱好者,呵呵,厉害.....
作者: redtek     时间: 2006-12-18 04:46
有一次买了个像机就一发不可收拾了,越来越发现这东东居然能把想法表达出来,体会那种生命被思想超越的感觉……

等有空一定多拍好看的照片给兄奉上~:)
作者: redtek     时间: 2006-12-18 04:47
ccwan兄说晚了,刚吃完……大冰激淋+咖啡,哈哈哈……
作者: redtek     时间: 2006-12-19 06:56    标题: 【80x86汇编语言学习笔记】

使用汇编语言显示彩色字符,然后用批处理包装起来实现不生成临时文件的方法:)


[原创][批处理单行显示多种颜色字符]
http://www.cn-dos.net/forum/viewthread.php?tid=25797&fpage=1


原贴写在上面地址了,下面是解释汇编代码的含义。
0B03:0100 MOV,AH,09              ;    09H号功能调用
0B03:0102 MOV,AL,2A              ;    把字符2AH(就是*号的16进制ASCII码)装入AL寄存器
0B03:0104 MOV,BH,00              ;    彩色显示卡支持0~7页,把0页装入BH寄存器
0B03:0106 MOV,BL,0E              ;     把颜色值送入BL寄存器。(黄颜色:1110二进制,转16进制为:0EH
0B03:0108 MOV,CX,0020            ;    这个符号的显示次数20H送入CX.
0B03:010B INT,10                 ;    显示器中断调用10H  
0B03:010D MOV,BL,0A              ;    换一种颜色 (1010绿色,转16进制是AH)
0B03:010F MOV,CX,000A            ;    和上面一样,显示次数
0B03:0112 INT,10
0B03:0114 INT,20                 ;    结束返回
光标任意定位原理也不难,但汇编的基础知识最重要~:)

[ Last edited by redtek on 2006-12-19 at 12:13 PM ]
附件 1: 1.GIF (2006-12-20 01:05, 9.94 K, 下载附件所需积分 1点 ,下载次数: 1)



作者: redtek     时间: 2006-12-20 01:12    标题: 【80x86汇编语言学习笔记】

根据测试,当调用了16位DOS程序以后,CMD 便被切换到了英文(美国)代码页:437,中文便无法显示了。

所以在代码中调用 Debug 来达到间接运行汇编代码功能后,当 Debug 退出后就进入了437代码页,给需要显示中文批处理带来麻烦。

如何既运行 16位 DOS 程序(如:Debug、Edit.com……等)还能正常显示中文呢?
解决办法如下:
@echo   %dbg%  off
::  以下代码在批处理起始部分(在 @echo off 语句后面)

chcp 437|graftabl 936 >NUL

::  后面的代码怎么用都可以
这样,再运行16位程序以及调用Debug完成特殊任务时,CMD窗口视觉上的大小不变、且正常显示中文。

(此刻不可以再输入中文了-CMD 输入法无效)

[ Last edited by redtek on 2006-12-19 at 12:22 PM ]
附件 1: 2.GIF (2006-12-20 01:21, 10.17 K, 下载附件所需积分 1点 ,下载次数: 1)



作者: redtek     时间: 2006-12-21 03:21    标题: 【80x86汇编语言学习笔记】

) 这几天汇编语言学习总结:


  学习汇编之前: 先学 Debug 基本使用等知识,可节省 70% 的入门时间。
          否则,学习的陡峭曲线非常高,如再遇到一本破书又无师指导,很难坚持下去并自学入门。


  开始学习汇编: 只学用得到的指令,用到再学,学的时候再找,会越学越深。
  

  对汇编书或资料的选择: 选择错了书如是自学,等同于拜了小人为师,不仅付出10倍努力且还不得旨领。
              往往这个时候就知难而退,有多少想学习汇编的人从此又放弃了。


  什么汇编的书是好书?  一般的教学书均是归纳、总结、分类的次序书写,这种书不适书入门,适合学术。
              要选择循序渐进的书做入门的书,往往一个概念得不到非常简明的解释,很可能又偏入岐途。
              先有简单一个例子,或是按照从简到深的能力一点点推进的书,是好书。
              没有 Debug 例子只有完全的理论的书是坏书。
              要讲到事物本质的书是好书:

              如:CPU是怎么执行指令的?
                为什么段地址*16+偏移量……为什么要乘16?要它所有推出来的步骤…99%的书都没有。
                高电平与低电平是如何代表指令的?
                一个读内存单元的动作CPU与地址总线、数据总线、控置总线是如何配合工作的?
                计算机中所有的设备(外设等)到底是怎么样的布局?如何工作的?(很多书都略掉了……)

              这些本质上的东西如果略掉,只留下指令应该怎么写,又不给出本质,那么学也白学。

              指令都是次要的,本质原理是最重要的。


  如果完全自学,没有学校教、没有老师教,没有论坛可以请教,只有一台计算机、一些书,那么完全自学汇编的唯一要求
就是这本书要非常好,要讲本质并讲透,不断的想、编……

  否则数十倍的努力去看那些破书并耗掉上百个小时,也不如好书里的一句话讲透了本质受用你一生的指点。



) 如何学好汇编?

  刚学几天,不知道。
  唯一感触非常多的就是,学的过程基础知识不能跳,表面上跳过去了后面也能看一部分,但到头来一定白学,还要回来。
  这点我是真的感触到了,并且回过头来再重看、重学的时候对汇编的兴趣会丢掉一半……



) 以上个人学习汇编几天的总结,权当瞎说~:)
作者: redtek     时间: 2006-12-21 03:52    标题: 【80x86汇编语言学习笔记】

从现在开始如果汇编基础知识不过关,就不再往后学。直到所有测试全部通过并理解以后再向后学习。


) 汇编语言基础知识测试

  1、1个CPU的寻址能力为8KB,它的地址总线宽度为?
  
    答: 8KB的寻址能力范围是: 0 ~~ 8191 B
       n个二进制位的寻址能力的宽度是 2的n次方,所以 2的x次方=8192KB。
       求得cpu的寻址能力为8kb,它地址总线宽度为 13。
       即 2的13次方为 8192B(8192/1024=8KB)


  2、1KB的存储器有多少个存储单元?存储单元的编号从多少到多少?

    答:1KB的存储器有 1024 个存储单元,一个存储单元可存 8 个 bit,就是8个二进制位。
      存储单元的编号是从0起始的,所以是 0 ~~ 1023。


  3、1KB的存储器可以存储多少个bit,多少个byte?

    答:1kb=1024B,可存 (1024B*8bit) 8192bit。
      还可以存 1024 个 Byte。


  4、1GB、1MB、1KB分别是多少 byte ?

    答:1GB =(1024b * 1024KB)mb * 1024mb
               =   1073741824 Byte

      1MB = 1048576 Byte

      1KB =  1024 Byte


  5、8080、8088、80286、80386地址总线宽度分别为16、20、24、32根,它们寻址能力为_KB、_MB、_MB、_GB。

    答:8080  为:64KB
      8088    为:1MB
      80286  为:16MB
      80386  为:4GB


  6、8080 8088 8086 80286 80386的数据总线宽度为8 8 16 16 32根,则它们一次可以传送的数据分别为多少B?

    答:8080  为:256B
      8088  为:256B
      8086  为:65536B
      80286 为:65536B
      80386 为:4294967296B


  7、从内存中读取1024字节的数据,8086至少要读多少次?80386至少要读多少次?

    答:8086 CPU数据总线宽度是 8,即每次只能传递 8 bit 数据。1024字节需要读1024次。
      80386 CPU数据总线宽度是32,每次传递 32 bit 数据,1024字节需要读256次。


  8、在存储器中,数据和程序以什么形式存放?

    答:数据和程序以二进制形式存放~:)



        

[ Last edited by redtek on 2006-12-20 at 03:42 PM ]
作者: redtek     时间: 2006-12-21 05:04    标题: 【80x86汇编语言学习笔记】

寄存器汇编指令练习:

  Quote:
C:\TEMP\debug>debug
-R
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 3D0100        CMP     AX,0001
-A
0AF5:0100 MOV AX,18
0AF5:0103 MOV AH,78
0AF5:0105 ADD AX,8
0AF5:0108 MOV AX,BX
0AF5:010A ADD AX,BX
0AF5:010C
-T=100

AX=0018  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 B478          MOV     AH,78
-T

AX=7818  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 050800        ADD     AX,0008
-T

AX=7820  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0108   NV UP EI PL NZ AC PO NC
0AF5:0108 89D8          MOV     AX,BX
-T

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010A   NV UP EI PL NZ AC PO NC
0AF5:010A 01D8          ADD     AX,BX
-T

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010C   NV UP EI PL ZR NA PE NC
0AF5:010C C28D8E        RET     8E8D
-Q

[ Last edited by redtek on 2006-12-20 at 04:06 PM ]
作者: redtek     时间: 2006-12-21 05:20    标题: 【80x86汇编语言学习笔记】

程序段中指令执行练习:

  Quote:
-A
0AF5:0100 MOV AX,8226
0AF5:0103 MOV BX,AX
0AF5:0105 ADD AX,BX
0AF5:0107
-T=100

AX=8226  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 89C3          MOV     BX,AX
-T

AX=8226  BX=8226  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 01D8          ADD     AX,BX
-T

AX=044C  BX=8226  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0107   OV UP EI PL NZ NA PO CY

最后当执行 ADD AX,BX后,发生“溢出”,16位寄存器只能存放4位16进制数据。



试验2:

  Quote:
0AF5:0100 MOV AX,001A
0AF5:0103 MOV BX,0026
0AF5:0106 ADD AL,BL
0AF5:0108 ADD AH,BL
0AF5:010A ADD BH,AL
0AF5:010C MOV AH,0
0AF5:010E ADD AL,85
0AF5:0110 ADD AL,93
0AF5:0112
-T=100

AX=001A  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 BB2600        MOV     BX,0026
-T

AX=001A  BX=0026  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI PL NZ NA PO NC
0AF5:0106 00D8          ADD     AL,BL
-T

AX=0040  BX=0026  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0108   NV UP EI PL NZ AC PO NC
0AF5:0108 00DC          ADD     AH,BL
-T

AX=2640  BX=0026  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010A   NV UP EI PL NZ NA PO NC
0AF5:010A 00C7          ADD     BH,AL
-T

AX=2640  BX=4026  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010C   NV UP EI PL NZ NA PO NC
0AF5:010C B400          MOV     AH,00
-T

AX=0040  BX=4026  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010E   NV UP EI PL NZ NA PO NC
0AF5:010E 0485          ADD     AL,85
-T

AX=00C5  BX=4026  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0110   NV UP EI NG NZ NA PE NC
0AF5:0110 0493          ADD     AL,93
-T

AX=0058  BX=4026  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0112   OV UP EI PL NZ NA PO CY
0AF5:0112 E94EFF        JMP     0063
-Q

以上学习完了简单的加指令:ADD


测试: 只能使用学过的(MOV、ADD)这两条汇编指令,最多使用4条指令,编程计算2的4次方。
-A
0AF5:0100 MOV AL,2
0AF5:0102 ADD AL,AL
0AF5:0104 ADD AL,AL
0AF5:0106 ADD AL,AL
0AF5:0108
-T=100

AX=0002  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0102   NV UP EI PL NZ NA PO NC
0AF5:0102 00C0          ADD     AL,AL
-T

AX=0004  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0104   NV UP EI PL NZ NA PO NC
0AF5:0104 00C0          ADD     AL,AL
-T

AX=0008  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI PL NZ NA PO NC
0AF5:0106 00C0          ADD     AL,AL
-T

AX=0010  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0108   NV UP EI PL NZ AC PO NC
结果10H(10进制的 16)。

[ Last edited by redtek on 2006-12-20 at 06:12 PM ]
作者: redtek     时间: 2006-12-21 07:39    标题: 【80x86汇编语言学习笔记】

( 16位CPU结构与物理地址计算  )

CPU结构特性:

  运算器一次最多可以处理16位的数据;
  寄存器的最大宽度为16位;
  寄存器和运算器之间的通路为16位。


8086CPU有20位地址总线,可能传送20位地址,达到1MB的寻址能力。
8086CPU又是16位结构,在内部一次性处理、传输、暂时存储的地址为16位。

从8086CPU的内部结构来看,如果将地址从内部简单地发出,那么它只能送出16位的地址,表现出的寻址能力只有64KB。

8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址。


在 8086PC机中,存储单元的地址用两个元素来描述:段地址和偏移地址。
可根据需要,将地址连续、起始地址为16的倍数的一组内存单元定义为一个段。


形容一个地址的标准讲法~:)

  (1) 数据存在内存 2000:1F60 单元中;
  (2) 数据存在内存的 2000 段中的 1F60 单元中。





检测练习:


  1、给定段地址为 0001H,公通过变化偏移地址寻址,CPU的寻址范围从哪里到哪里?

    答: 0001H * 16 = 10H (段)
       偏移量为16位,寻址范围为:0 ~~ FFFFH
       段地址 * 16 + 偏移地址=物理地址
       所以,CPU的物理寻址范围是: 10H ~~ 10FFFFH


  2、有一数据存放在内存 20000H 单元中,现给定段地址为 SA,若想用偏移地址寻到此单元。SA应满足的条件是:
    最小为?最大为?



    答:范围取值过程:
      --------------------------------------
        1001H * 16 +  FFF0H = 20000H
        
           …… ……
        
        1FFEH * 16 +    20H = 20000H
        1FFFH * 16 +    10H = 20000H
        2000H * 16 +     0H = 20000H
      --------------------------------------
      
      根据以上同理,已知:1000H * 16 + 10000H = 20000H 会发生16位溢出
      
      推出计算公式如下:
      
      ( 20000H - FFFFH ) / 16 + 1H = 为 SA 段地址最小取值
      
      
      所以,SA 最小取值为: 1FFEH
         SA 最大取值为: 2000H
      
         偏移量最小取值为:   0H
         偏移量最大取值为:FFF0H

         偏移量每次递增为:  10H


本想这道题不做了,昨天是绕进去了~:)
今天早上做的时候发现这道题得做,虽然做完了觉得简单。
做的过程中……一会儿16进制、一会儿又是10进制,来回来去的算……
当做完以后,发现了很多细节以及运算上以前没有注意到的地方。

深感基础必须扎实!基础越是扎实,后面的路就走得越远~:)



查看内存中的内容

根据资料给出:PC机主板上的ROM中写有一个生产日期,在内存 FFF00H~~FFFFFH 的某几个单元中,
请找到这个生产日期并试图改变它。

根据上面的计算原理,将物理内存地址“拆”成  段地址:偏移量  所表示的地址,使用Debug操作如下:

  Quote:
-D FFF0:0000
FFF0:0000  EF 53 FF 65 F0 4D F8 41-F8 59 EC 39 E7 59 F8 2E   .S.e.M.A.Y.9.Y..
FFF0:0010  E8 D2 EF 00 E0 F2 E6 6E-FE 53 FF 53 FF A4 F0 C7   .......n.S.S....
FFF0:0020  EF 00 00 5A EF 0F 07 F3-EE F3 EE F3 EE 18 07 F3   ...Z............
FFF0:0030  EE F3 EE 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:0040  00 00 00 00 00 00 00 00-E9 01 12 00 00 00 00 00   ................
FFF0:0050  CA 04 00 CF E9 33 EA 00-00 28 43 29 32 30 30 30   .....3...(C)2000
FFF0:0060  41 4D 49 2C 37 37 30 2D-32 34 36 2D 38 36 30 30   AMI,770-246-8600
FFF0:0070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-D
FFF0:0080  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:0090  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:00A0  00 00 00 00 00 00 00 00-00 00 E9 9F 11 00 00 00   ................
FFF0:00B0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:00C0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:00D0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:00E0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:00F0  EA 5B E0 00 F0 30 34 2F-30 32 2F 30 34 00 FC 00   .[...04/02/04...
-D
FFF0:0100  34 12 00 00 00 00 00 00-00 00 00 00 00 00 00 00   4...............
FFF0:0110  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:0120  70 00 2E 8E 06 30 00 BF-7F 01 B9 02 00 AB 47 47   p....0........GG
FFF0:0130  E2 FB CB 56 50 51 52 57-55 1E 06 53 8B EC 8B 76   ...VPQRWU..S...v
FFF0:0140  12 2E 8E 1E 30 00 8B 44-02 A2 22 00 88 26 08 01   ....0..D.."..&..
FFF0:0150  8B 34 C4 1E 18 00 26 8A-47 01 26 8A 67 0D 26 8B   .4....&.G.&.g.&.
FFF0:0160  4F 12 26 8B 57 14 97 26-8A 47 02 2E 3A 04 73 2C   O.&.W..&.G..:.s,
FFF0:0170  98 D1 E0 03 F0 97 26 C4-7F 0E FC 2E FF 54 01 72   ......&......T.r
-D
FFF0:0180  02 B4 01 2E 8E 1E 30 00-C5 1E 18 00 89 47 03 5B   ......0......G.[
FFF0:0190  07 1F 5D 5F 5A 59 58 5E-83 C4 02 CB E8 26 00 EB   ..]_ZYX^.....&..
FFF0:01A0  E2 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:01B0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FFF0:01C0  EA AE 10 A7 00 B0 03 C4-1E 18 00 26 29 4F 12 B4   ...........&)O..
FFF0:01D0  81 F9 C3 00 0B E1 01 E1-01 E1 01 D5 00 5C 01 9F   .............\..
FFF0:01E0  01 E1 01 33 02 E3 01 E3-01 E1 01 1A E1 01 E1 01   ...3............
FFF0:01F0  E1 01 D5 00 44 02 AE 01-E1 01 E1 01 49 02 49 02   ....D.......I.I.
-Q

[ Last edited by redtek on 2006-12-21 at 03:14 PM ]
作者: redtek     时间: 2006-12-22 04:40    标题: 【80x86汇编语言学习笔记】

寄存器与内存访问

存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。
高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。

任何两个地址连续的内存单元,N号单元和N+1号单元,可以将它们看成两个内存单元,
也可以看成一个地址为N的字单元中的高位字节单元和低位字节单元。


DS,通常用来存放要访问数据的段的地址。
例如:要读取  10000H 单元的内容,可以用如下的程序段进行:
-A
0AF5:0100 MOV BX,1000
0AF5:0103 MOV DS,BX
0AF5:0105 MOV AL,[0]
0AF5:0108
-T=100

AX=0000  BX=1000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 8EDB          MOV     DS,BX
-T

AX=0000  BX=1000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 A00000        MOV     AL,[0000]                          DS:0000=64
-T

AX=0064  BX=1000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0108   NV UP EI PL NZ NA PO NC
上面是读物理地址 10000H 单元内容,按 “段地址:偏移量” 来访问就是 1000:0000
因为其物理地址的计算为: 1000H*16+0000=10000H ,这个计算由CPU交给加法器来完成。

8086CPU不支持将数据直接送入段寄存器的操作(如: MOV DS,1000),这是非法指令。
所以只能按上面代码的间接用寄存器来进行中转操作。


测试:将 AL 中的数据送入内存单元 10000H
-R
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B040          MOV     AL,40
-A
0AF5:0100 MOV AL,40
0AF5:0102 MOV BX,1000
0AF5:0105 MOV DS,BX
0AF5:0107 MOV [0],AL
0AF5:010A
-T=100

AX=0040  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0102   NV UP EI PL NZ NA PO NC
0AF5:0102 BB0010        MOV     BX,1000
-T

AX=0040  BX=1000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 8EDB          MOV     DS,BX
-T

AX=0040  BX=1000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0107   NV UP EI PL NZ NA PO NC
0AF5:0107 A20000        MOV     [0000],AL                          DS:0000=64
-T

AX=0040  BX=1000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=010A   NV UP EI PL NZ NA PO NC
0AF5:010A CD21          INT     21

-D 1000:0000 0
1000:0000  40                                                @
-Q
以上是字节型数据的传送。

字的传送


8086CPU是16位结构,有16根数据线,一次性可以传送16位的数据。

也就是说,可以一次性传送一个字。只要在 mov 指令中给出 16位的寄存器就可以进行16 位数据的传送了。


以下是字的传送练习与内存分析:
0AF5:0100 AX,1000
0AF5:0103 DS,AX
0AF5:0105 MOV AX,[0]
0AF5:0108 MOV BX,[2]
0AF5:010C MOV CX,[1]
0AF5:0110 ADD BX,[1]
0AF5:0114 ADD CX,[2]
0AF5:0118
-T=100

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 8ED8          MOV     DS,AX
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 A10000        MOV     AX,[0000]                          DS:0000=1123
-T

AX=1123  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0108   NV UP EI PL NZ NA PO NC
0AF5:0108 8B1E0200      MOV     BX,[0002]                          DS:0002=6622
-T

AX=1123  BX=6622  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=010C   NV UP EI PL NZ NA PO NC
0AF5:010C 8B0E0100      MOV     CX,[0001]                          DS:0001=2211
-T

AX=1123  BX=6622  CX=2211  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0110   NV UP EI PL NZ NA PO NC
0AF5:0110 031E0100      ADD     BX,[0001]                          DS:0001=2211
-T

AX=1123  BX=8833  CX=2211  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0114   OV UP EI NG NZ NA PE NC
0AF5:0114 030E0200      ADD     CX,[0002]                          DS:0002=6622
-T

AX=1123  BX=8833  CX=8833  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0118   OV UP EI NG NZ NA PE NC
寄存器与段间数据传送
0AF5:0100 MOV AX,1000
0AF5:0103 MOV DS,AX
0AF5:0105 MOV [0],CS
0AF5:0109
-R
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80010        MOV     AX,1000
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 8ED8          MOV     DS,AX
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 8C0E0000      MOV     [0000],CS                          DS:0000=000
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0109   NV UP EI PL NZ NA

-D 1000:0000
1000:0000  [color=Red]F5 0A [/color]00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-Q
“MOV  段寄存器,内存单元” 数据传送分析

  Quote:
-d 1000:000
1000:0000  F5 0A 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80010        MOV     AX,1000
-a
0AF5:0100 MOV AX,1000
0AF5:0103 MOV DS,AX
0AF5:0105 MOV DS,[0]
0AF5:0109
-R
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80010        MOV     AX,1000
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 8ED8          MOV     DS,AX
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 8E1E0000      MOV     DS,[0000]                          DS:0000=0AF5
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0109   NV UP EI PL NZ NA PO NC

[ Last edited by redtek on 2006-12-21 at 09:33 PM ]
作者: redtek     时间: 2006-12-22 10:58    标题: 【80x86汇编语言学习笔记】

物理地址 = 段地址*16 + 偏移量

根据上面原理灵活运用计算物理地址的方法,来用不同的段地址与偏移量取“同”一数据:

  Quote:
C:\TEMP\debug>DEBUG
-D 0000:0000
0000:0000  68 10 A7 00 8B 01 70 00-16 00 A9 03 8B 01 70 00   h.....p.......p.
0000:0010  8B 01 70 00 B9 06 12 02-40 07 12 02 FF 03 12 02   ..p.....@.......
0000:0020  46 07 12 02 0A 04 12 02-3A 00 A9 03 54 00 A9 03   F.......:...T...
0000:0030  6E 00 A9 03 88 00 A9 03-A2 00 A9 03 FF 03 12 02   n...............

0000:0040  A9 08 12 02 A4 09 12 02-AA 09 12 02 5D 04 12 02   ............]...
0000:0050  B0 09 12 02 0D 02 E1 02-C4 09 12 02 8B 05 12 02   ................
0000:0060  0E 0C 12 02 14 0C 12 02-1F 0C 12 02 AD 06 12 02   ................
0000:0070  AD 06 12 02 A4 F0 00 F0-37 05 12 02 94 2D 00 C0   ........7....-..

变换段地址,取上面红色标注地址:
计算上面红色段地址*16+偏移量 = 物理地址,
然后再将物理地址 / 16 ,偏移量取0000,得到变换地址如下:


-D 0004:0000
0004:0000  A9 08 12 02 A4 09 12 02-AA 09 12 02 5D 04 12 02   ............]...
0004:0010  B0 09 12 02 0D 02 E1 02-C4 09 12 02 8B 05 12 02   ................
0004:0020  0E 0C 12 02 14 0C 12 02-1F 0C 12 02 AD 06 12 02   ................
0004:0030  AD 06 12 02 A4 F0 00 F0-37 05 12 02 94 2D 00 C0   ........7....-..
0004:0040  72 10 A7 00 7C 10 A7 00-4F 03 59 05 8A 03 59 05   r...|...O.Y...Y.
0004:0050  17 03 59 05 86 10 A7 00-90 10 A7 00 9A 10 A7 00   ..Y.............
0004:0060  B8 10 A7 00 54 02 70 00-F2 04 91 D0 B8 10 A7 00   ....T.p.........
0004:0070  B8 10 A7 00 B8 10 A7 00-40 01 27 04 70 09 C8 D8   ........@.'.p...
-D 0005:0000
0005:0000  B0 09 12 02 0D 02 E1 02-C4 09 12 02 8B 05 12 02   ................
0005:0010  0E 0C 12 02 14 0C 12 02-1F 0C 12 02 AD 06 12 02   ................
0005:0020  AD 06 12 02 A4 F0 00 F0-37 05 12 02 94 2D 00 C0   ........7....-..
0005:0030  72 10 A7 00 7C 10 A7 00-4F 03 59 05 8A 03 59 05   r...|...O.Y...Y.
0005:0040  17 03 59 05 86 10 A7 00-90 10 A7 00 9A 10 A7 00   ..Y.............
0005:0050  B8 10 A7 00 54 02 70 00-F2 04 91 D0 B8 10 A7 00   ....T.p.........
0005:0060  B8 10 A7 00 B8 10 A7 00-40 01 27 04 70 09 C8 D8   ........@.'.p...
0005:0070  EA AE 10 A7 00 EE 00 F0-B8 10 A7 00 A6 24 02 CD   .............$..
-D 0008:0000
0008:0000  72 10 A7 00 7C 10 A7 00-4F 03 59 05 8A 03 59 05   r...|...O.Y...Y.
0008:0010  17 03 59 05 86 10 A7 00-90 10 A7 00 9A 10 A7 00   ..Y.............
0008:0020  B8 10 A7 00 54 02 70 00-F2 04 91 D0 B8 10 A7 00   ....T.p.........
0008:0030  B8 10 A7 00 B8 10 A7 00-40 01 27 04 70 09 C8 D8   ........@.'.p...
0008:0040  EA AE 10 A7 00 EE 00 F0-B8 10 A7 00 A6 24 02 CD   .............$..
0008:0050  B8 10 A7 00 B8 10 A7 00-B8 10 A7 00 B8 10 A7 00   ................
0008:0060  B8 10 A7 00 B8 10 A7 00-B8 10 A7 00 B8 10 A7 00   ................
0008:0070  B8 10 A7 00 B8 10 A7 00-B8 10 A7 00 B8 10 A7 00   ................

[ Last edited by redtek on 2006-12-21 at 10:01 PM ]
作者: redtek     时间: 2006-12-22 23:34    标题: 【80x86汇编语言学习笔记】



push ax 的执行由下面两步完成:

1、SP=SP-2,SS:SP指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶;
2、将ax中的内容送入 SS:SP 指向的内存单元处,SS:SP 此时指向新栈顶。

两个寄存器: 段寄存器 SS 和 寄存器 SP,栈顶的段地址存放在 SS 中,偏移地址存放在 SP 中。
任意时刻, SS:SP 指向栈顶元素。

push 和 pop 指令对栈的操作以及为栈设置空间

  Quote:
C:\TEMP\debug>DEBUG
-R
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80010        MOV     AX,1000
-A
0AF5:0100 MOV AX,1000
0AF5:0103 MOV SS,AX
0AF5:0105 MOV SP,0010
0AF5:0108 PUSH AX
0AF5:0109 PUSH BX
0AF5:010A PUSH DS
0AF5:010B
-R
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80010        MOV     AX,1000
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 8ED0          MOV     SS,AX
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=0010  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=1000  CS=0AF5  IP=0108   NV UP EI PL NZ NA PO NC
0AF5:0108 50            PUSH    AX
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=000E  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=1000  CS=0AF5  IP=0109   NV UP EI PL NZ NA PO NC
0AF5:0109 53            PUSH    BX
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=000C  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=1000  CS=0AF5  IP=010A   NV UP EI PL NZ NA PO NC
0AF5:010A 1E            PUSH    DS
-T

AX=1000  BX=0000  CX=0000  DX=0000  SP=000A  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=1000  CS=0AF5  IP=010B   NV UP EI PL NZ NA PO NC

编程(1)将 10000H ~~ 1000FH 这段空间当作栈,初始状态栈是空的;
   (2)设置 AX=001AH,BX=001BH;
   (3)将 AX、BX中的数据入栈;
   (4)然后将AX、BX清零;
   (5)从栈中恢复AX、BX原来的内容。


  Quote:
-A
0AF5:0100 MOV AX,1000
0AF5:0103 MOV SS,AX
0AF5:0105 MOV SP,0010
0AF5:0108 MOV AX,001A
0AF5:010B MOV BX,001B
0AF5:010E PUSH AX
0AF5:010F PUSH BX
0AF5:0110 XOR AX,AX
0AF5:0112 XOR BX,BX
0AF5:0114 POP BX
0AF5:0115 POP AX
0AF5:0116

上面标注绿色的指令是清零指命,利用 XOR 的性质清零,速度快于 MOV AX,0000 ……

MOV  AX,0 也可以清零,但其机器码占用了3个字节,如果想让编译出来的程序节省一个字节,则不用这个命令。

经过分析,可以看到下面红色的部分为机器码,占用了3个字节。(能省一个字节就省)

  Quote:
0AF5:0103 B80000        MOV     AX,0000

再看一下SUB (减)指令:

SUB  AX,AX  也可以让AX寄存器清零。

下面是 SUB 操作,红色部分是SUB的机器码:

  Quote:
0AF5:0103 29C0          SUB     AX,AX

再试验一下 XOR (或)指令:

XOR  AX,AX   将 AX 寄存器内容清零

  Quote:
0AF5:0103 31C0          XOR     AX,AX

上面的清零的机器码只占用了2个字节。


一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么都不是。
关键在于CPU中寄存器的设置,即: CS IP SS SP DS 的指向。


如果寄存器知识没有学好,很可能往下学习就是寸步难行~:)

[ Last edited by redtek on 2006-12-22 at 04:40 PM ]
作者: redtek     时间: 2006-12-23 06:46
现在开始学着写第一个程序~:)

坚持着学就学下来了,越学越难,越难越学。如果没有坚持下来,等于浪费生命~:)
想想阿香婆是怎么熬辣酱的,哈哈……

从刚学到现在,按书上的深度来讲,连入门都没有到~:)
不管,接着往前走~:)
作者: redtek     时间: 2006-12-23 08:08    标题: 【80x86汇编语言学习笔记】

汇编语言程序从写出到最终执行的过程

1) 编写汇编源程序
2) 对源程序进行编译连接
3) 执行




在汇编语言源程序中,包含两种指令: 
汇编指令伪指令


写第一个代码

  Quote:
;-----------------------------------------
;
;       Masm 5.0  1.asm
;
;-----------------------------------------


assume cs:codesg

codesg segment

        mov ax,0123H
        mov bx,0456H
        add ax,bx
        add ax,ax

        mov ax,4c00H
        int 21H

codesg ends

       end

编译过程:(Masm 5.0)

  Quote:
C:\Masm50>masm 1.asm
Invalid keyboard code specified
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.

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

  51040 + 450320 Bytes symbol space free

      0 Warning Errors
      0 Severe  Errors

C:\Masm50>type 1.asm|clip

C:\Masm50>link 1

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

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

可执行文件:

  Quote:
C:\Masm50>dir 1.*
Volume in drive C is DISK-C
Volume Serial Number is 4089-CA39

Directory of C:\Masm50

2006-12-22  20:48               313 1.asm
2006-12-22  20:50               527 1.EXE
2006-12-22  20:48                66 1.OBJ

跟踪刚才编译的 1.exe 可执行文件过程:

  Quote:
AX=0000  BX=0000  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 B82301        MOV     AX,0123
-U
0B49:0000 B82301        MOV     AX,0123
0B49:0003 BB5604        MOV     BX,0456
0B49:0006 03C3          ADD     AX,BX
0B49:0008 03C0          ADD     AX,AX
0B49:000A B8004C        MOV     AX,4C00
0B49:000D CD21          INT     21
0B49:000F 7083          JO      FF94
0B49:0011 C406B8C8      LES     AX,[C8B8]
0B49:0015 05508D        ADD     AX,8D50
0B49:0018 46            INC     SI
0B49:0019 8050E83E      ADC     BYTE PTR [BX+SI-18],3E
0B49:001D 0D83C4        OR      AX,C483
-R
AX=0000  BX=0000  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 B82301        MOV     AX,0123
-T

AX=0123  BX=0000  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0003   NV UP EI PL NZ NA PO NC
0B49:0003 BB5604        MOV     BX,0456
-T

AX=0123  BX=0456  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0006   NV UP EI PL NZ NA PO NC
0B49:0006 03C3          ADD     AX,BX
-P

AX=0579  BX=0456  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0008   NV UP EI PL NZ NA PO NC
0B49:0008 03C0          ADD     AX,AX
-P

AX=0AF2  BX=0456  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000A   NV UP EI PL NZ AC PO NC
0B49:000A B8004C        MOV     AX,4C00
-P

AX=4C00  BX=0456  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000D   NV UP EI PL NZ AC PO NC
0B49:000D CD21          INT     21
-P

Program terminated normally
-Q

头晕,PSP……

操作系统的外壳……
程序是如何执行的?

1) 在DOS中直接执行 1.exe 时,是正在运行的 command ,将 1.exe 中的程序加载入内存;

2) command 设置 CPU 的 CS:IP 指向程序的第一条指令(即程序的入口),从而使程序得以运行;

3) 程序运行结束后,返回到 command 中, CPU 继续运行 command。


可执行程序被加载的过程:

1) 找到一段起始地址 SA:0000 的容量足够的空闲内存;
2) 在这段内存区的前 256 个字节中,创建一个称为程序段前缀(PSP)的数据区,DOS要利用PSP来和被加载程序进行通信;

   ---------内存---------
   ////////
   ////////
sa:  ------------------------
      PSP
   ------------------------  SA + 10H:0
CS:IP
      程

      序

   ------------------------



3) 从这段内存区的 256 字节处开始(在PSP的后面),将程序装入。
   程序的地址被设为 SA + 10H : 0 ;
   
   空闲的内存区从 SA:0 开始, 0~~255 字节为 PSP,从 256 字节处开始存放程序。
   为更好地区分 PSP 和程序,DOS一般将它们划分到不同的段中,所以有这样的地址安排:

   空闲内存区: SA : 0
     PSP区:  SA : 0
     程序区: SA + 10H : 0

   注意: PSP区和程序区虽然物理地址连续,却有不同的段地址。


4) 将该内存区的段地址存入 DS 中,初始化其他相关寄存器后,设置 CS:IP 指向程序入口。

PSP 占 256(100H)字节,所以程序的物理地址是:SA*16+0+256=SA*16+16*16+0=(SA+16)*16+0

可用段地址和偏移地址表示为:SA+10:0。

理解:设 DS=129E ,问 PSP 地址? 和程序的地址是多少?

   程序加载后,DS中存放着程序所在的内存区的段地址,这个内存区的偏移地址为0,则程序所在的内存区的地址为 DS:0
   程序地址: 129E*16+0+256=129E0+100H=12AE0H(物理地址)=12AE:0=CS:0(相对地址)
   PSP 地址: 129E:0

但是,上面求程序所在地址(非物理地址)可以如下变通:

求物理地址: 129E*16相当于(129E0),而加256相当于(加100H),
       129E0H
           100H
     --------------------- +
       12AE0H 但求 段:偏移量 地址方式,则还要再去一个0(相当于除16),所以应如下变通:
129EH + 10H = 12AE:0000 这样就求出了: 段地址:偏移量

上面就是程序是如何分配程序地址的计算原理的过程~:)

怎么可以“直观”的看到PSP的“标置”?PSP的头两个字节是 CD 20

  Quote:
C:\Masm50>DEBUG 1.EXE
-R
AX=0000  BX=0000  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 B82301        MOV     AX,0123
-D DS:0000
0B39:0000  CD 20 FF 9F 00 9A F0 FE-1D F0 4F 03 59 05 8A 03   . ........O.Y...
0B39:0010  59 05 17 03 59 05 48 05-01 01 01 00 02 FF FF FF   Y...Y.H.........
0B39:0020  FF FF FF FF FF FF FF FF-FF FF FF FF 06 0B 4C 01   ..............L.
0B39:0030  19 0A 14 00 18 00 39 0B-FF FF FF FF 00 00 00 00   ......9.........
0B39:0040  05 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0B39:0050  CD 21 CB 00 00 00 00 00-00 00 00 00 00 20 20 20   .!...........
0B39:0060  20 20 20 20 20 20 20 20-00 00 00 00 00 20 20 20           .....
0B39:0070  20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00           ........
-Q

编译成 .exe 文件,用 Debug 调试代码

  Quote:
              ASSUME    CS:CODESG
      CODESG  SEGMENT

              MOV       AX,2000H
              MOV       SS,AX
              MOV       SP,0
              ADD       SP,4
              POP       AX
              POP       BX
              PUSH      AX
              PUSH      BX
              POP       AX
              POP       BX

              MOV       AX,4C00H
              INT       21H

      CODESG  ENDS
              END

因为栈空间分配大小的原因,编译成 .exe 文件后在 MS-DOS 6.22 下调试正常,在Windows CMD 内调试出错,增加栈空间即可。

[ Last edited by redtek on 2006-12-23 at 09:05 AM ]
作者: redtek     时间: 2006-12-23 22:44    标题: 【80x86汇编语言学习笔记】

汇编指令描述方式

内存单元的描述



mov ax,[0]

将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在ds中。


描述练习


1) ax中的内容为0010H,描述它
   (ax)=0010H

2) 2000:1000 处的内容为 0010H
   (21000H)= 0010H

3) mov ax,[2]
   (ax)= ((ds)* 16 + 2)

4) mov [2],ax
   ((ds)* 16 + 2)= (ax)

5) add ax,2
   (ax)=(ax)+ 2

6) add ax,bx
   (ax)=(ax)+(bx)

7) push ax
   (sp)=(sp)- 2
   ((ss)* 16 + (sp))= (ax)

8) pop ax
   (ax)=((ss)* 16 + (sp))
   (sp)=(sp)+ 2

为什么要这样描述?

这种描述方式可以更深的理解指令的物理内存操作原理和所有过程,它是理解更多内容与原理的必须基础。

[ Last edited by redtek on 2006-12-23 at 10:06 AM ]
作者: ccwan     时间: 2006-12-24 00:10
redtek笔耕不辍,我来鼓励一下!
作者: redtek     时间: 2006-12-24 01:58
多谢ccwan兄不断鼓励~:)
作者: redtek     时间: 2006-12-24 02:16    标题: 【80x86汇编语言学习笔记】

循环指令与编译器


下面代码 Masm 5.0 编译通过~:)

  Quote:
;------------------------------------------------
;             计算 2的2次方
;-------------------------------------------------

              ASSUME    CS:CODE

        CODE  SEGMENT

              MOV       AX,2
              ADD       AX,AX

              MOV       AX,4C00H
              INT       21H

        CODE  ENDS
              END

但是,如果要使用 ADD 指令计算几好百个次方,是不是要输出几百个指令?
可以用 Loop 循环指令模拟一下,这东东稍微有点儿像批处理里的 Goto 语句,哈哈……

先一步一步慢慢来,先分析上面代码编译之后的样子~:)

象 “ASSUME    CS:CODE” 与 “CODE  SEGMENT” 等都是汇编伪指令,CPU不认识,它由 Masm 编译器编译时“转化”。

  Quote:
C:\Masm50>debug LOOP.EXE
-r
AX=0000  BX=0000  CX=000A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3C  ES=0B3C  SS=0B4C  CS=0B4C  IP=0000   NV UP EI PL NZ NA PO NC
0B4C:0000 B80200        MOV     AX,0002
-t

AX=0002  BX=0000  CX=000A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3C  ES=0B3C  SS=0B4C  CS=0B4C  IP=0003   NV UP EI PL NZ NA PO NC
0B4C:0003 03C0          ADD     AX,AX
-t

AX=0004  BX=0000  CX=000A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3C  ES=0B3C  SS=0B4C  CS=0B4C  IP=0005   NV UP EI PL NZ NA PO NC
0B4C:0005 B8004C        MOV     AX,4C00
-t

AX=4C00  BX=0000  CX=000A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3C  ES=0B3C  SS=0B4C  CS=0B4C  IP=0008   NV UP EI PL NZ NA PO NC
0B4C:0008 CD21          INT     21
-p

Program terminated normally
-q

下面使用循环计算2的12次方

  Quote:
;----------------------------------------
;    2 ^ 12 次方计算
;----------------------------------------

              ASSUME    CS:CODE

        CODE  SEGMENT

              MOV       AX,2

              MOV       CX,11
          S:  ADD       AX,AX
              LOOP      S

              MOV       AX,4C00H
              INT       21H

        CODE  ENDS
              END

Loop 是循环,S:是循环标号, mov cx,11 ,每次循环一次cx寄存器值被递减。

执行顺序:执行到 Loop 时,CX值先减1,然后如果判断不为0,则跳转到标号。
它是如何跳转到标号的?它会修改 IP 的指向,cpu 执行 CS:IP 所指向的指令,然自就“返回”了标号处。


在Debug中跟踪Loop指令

  Quote:
C:\Masm50>debug loop12.exe
-r
AX=0000  BX=0000  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3D  ES=0B3D  SS=0B4D  CS=0B4D  IP=0000   NV UP EI PL NZ NA PO NC
0B4D:0000 B80200        MOV     AX,0002
-u cs:0 D
0B4D:0000 B80200        MOV     AX,0002
0B4D:0003 B90B00        MOV     CX,000B
0B4D:0006 03C0          ADD     AX,AX
0B4D:0008 E2FC          LOOP    0006
0B4D:000A B8004C        MOV     AX,4C00
0B4D:000D CD21          INT     21
-R
AX=0000  BX=0000  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3D  ES=0B3D  SS=0B4D  CS=0B4D  IP=0000   NV UP EI PL NZ NA PO NC
0B4D:0000 B80200        MOV     AX,0002
-T

AX=0002  BX=0000  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3D  ES=0B3D  SS=0B4D  CS=0B4D  IP=0003   NV UP EI PL NZ NA PO NC
0B4D:0003 B90B00        MOV     CX,000B
-T

AX=0002  BX=0000  CX=000B  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3D  ES=0B3D  SS=0B4D  CS=0B4D  IP=0006   NV UP EI PL NZ NA PO NC
0B4D:0006 03C0          ADD     AX,AX
-T

AX=0004  BX=0000  CX=000B  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3D  ES=0B3D  SS=0B4D  CS=0B4D  IP=0008   NV UP EI PL NZ NA PO NC
0B4D:0008 E2FC          LOOP    0006
-T

AX=0004  BX=0000  CX=000A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3D  ES=0B3D  SS=0B4D  CS=0B4D  IP=0006   NV UP EI PL NZ NA PO NC
0B4D:0006 03C0          ADD     AX,AX

上面代码中的棕色代码与汇编源代码中的伪指令的区别。
绿色代码为执行Loop时的跳转与 IP 的指向。

看循环指令结果时,也可以使用 Debug P 指令来跟踪最后的结果。

下面是计算 123 * 236

  Quote:
;------------------------------------------------
;
;             123 * 236
;
;------------------------------------------------

              assume    cs:code

        code  segment

              mov       ax,0
              mov       cx,123
          s:  add       ax,236
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end

如果下面这样写结果也一样正确:
              mov       cx,236
          s:  add       ax,123
但,相当于循环了 236 遍,如果 mov cx,123 ,则相当于只循环了123遍,速度稍快一些。

在Debug中跟踪下面编译的代码

  Quote:
              assume    cs:code

        code  segment

              mov       ax,0ffffh
              mov       ds,ax
              mov       bx,6

              mov       al,[bx]     ; 以上,设置(al)=((ds*16)+(bx)), (ah)=0
              mov       ah,0

              mov       dx,0        ; 累加寄存器清0

              mov       cx,3        ; 循环3次
          s:  add       dx,ax       ; 以上累加计算(ax)*3
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end

可以看到,Loop指令的每一步是如何工作的……

  Quote:
AX=0034  BX=0006  CX=0003  DX=0034  SP=0000  BP=0000  SI=0000  DI=0000
DS=FFFF  ES=0B3D  SS=0B4D  CS=0B4D  IP=0014   NV UP EI PL NZ NA PO NC
0B4D:0014 E2FC          LOOP    0012
-t

(当上面这句 Loop 0012 执行时,寄存器状态如下面所示)

AX=0034  BX=0006  CX=0002  DX=0034  SP=0000  BP=0000  SI=0000  DI=0000
DS=FFFF  ES=0B3D  SS=0B4D  CS=0B4D  IP=0012   NV UP EI PL NZ NA PO NC
0B4D:0012 03D0          ADD     DX,AX
-t

(上面是到了标号处的指令)

AX=0034  BX=0006  CX=0002  DX=0068  SP=0000  BP=0000  SI=0000  DI=0000
DS=FFFF  ES=0B3D  SS=0B4D  CS=0B4D  IP=0014   NV UP EI PL NZ NA PO NC
0B4D:0014 E2FC          LOOP    0012
-t

AX=0034  BX=0006  CX=0001  DX=0068  SP=0000  BP=0000  SI=0000  DI=0000
DS=FFFF  ES=0B3D  SS=0B4D  CS=0B4D  IP=0012   NV UP EI PL NZ NA PO NC
0B4D:0012 03D0          ADD     DX,AX
-t

AX=0034  BX=0006  CX=0001  DX=009C  SP=0000  BP=0000  SI=0000  DI=0000
DS=FFFF  ES=0B3D  SS=0B4D  CS=0B4D  IP=0014   NV UP EI PL NZ NA PE NC
0B4D:0014 E2FC          LOOP    0012
-t

如果(cx)=(cx)-1 ,当(cx)=0时: 因为(cx)=0,Loop指令不跳转。向下执行。

[ Last edited by redtek on 2006-12-23 at 05:27 PM ]
作者: redtek     时间: 2006-12-24 06:46    标题: 关于批处理嵌入超长汇编代码

[原创][批处理单行显示多种颜色字符]
http://www.cn-dos.net/forum/view ... ghlight=&page=2

上面贴子的第27楼:

  qzwqzw兄提到了:“从可读性上来讲,可以将汇编脚本分行下载批处理前段,这样的形式可以更好的注释”。

  感谢qzwqzw兄精彩的指点,原来水平无法实现这个功能,现在可以实现了:)

  而且根据qzwqzw兄指点,如果按这种方法做,可以在 .Bat 批处理中嵌入超长汇编代码,如嵌入数百行……

  也就是说可以执行非常复杂的汇编了,且还与批处理合二为一。

  而且,这种内嵌方式也解决了纯 MS-DOS 下命令行长度不能过长的一大障碍~:)


  如果哪天急了,还可以汇编、批处理、VBS三合一合写一个代码(晕晕……)

(刚才测试了一下,有一些障碍~:)

[ Last edited by redtek on 2006-12-23 at 06:14 PM ]
作者: redtek     时间: 2006-12-24 07:27
简单做了一个批处理内嵌汇编代码的实验程序,其最主要的目地是为了完成特殊任务,同时不生成临时文件。
@echo off

        :: 批处理嵌汇编代码实验
        :: Redtek 2006   bbs.cn-dos.net

:start
        ::
        ::
        ::  执行批处理代码段,绕开汇编代码段
        Goto :BatStart

:masm
        a
        mov ah,09
        mov dx,0109
        int 21
        int 20
        db "My name is Redtek!$"
       
        g
        q


:BATStart

        ::  装入当前批处理自身,并定cs代码段为 :masm 批处理标签段内。
        ::  echo exit|cmd /k prompt n %~s0$_l$_ …… (夭折喽~~)
        ::  想到另一个办法~:)
       
        echo exit|cmd /k more +10 %~s0|debug

::        Debug 执行后会显示所执行的过程,此功能可以用将来调用汇编代码来完成复杂计算或写盘,
::        或调用Debug进行非显示方式的后台特殊工作。
::
::
::

作者: redtek     时间: 2006-12-24 10:13    标题: 【80x86汇编语言学习笔记】

连续的累加指定的内存单元,求和例程。
用 Debug 分析过程。
              assume    cs:code

        code  segment

              mov       ax,0ffffh
              mov       ds,ax
              mov       bx,0
              mov       dx,0

              mov       cx,12
          s:  mov       al,[bx]
              mov       ah,0
              add       dx,ax
              inc       bx
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end
指令区别:

) Masm
  mov  ax,[0]
  将值0传送给ax寄存器

) Debug 汇编直译
  mov  ax,[0]
  相当于: (ax)= ((ds)*16+0)



) Debug 汇编直译
  mov  ax,FFFF
  将 FFFFH 传送到 ax 寄存器

) Masm
  mov  ax,0FFFFH
  功能同上。但在Masm汇编代码中,数据不能以字母开头,所以前必须加0


解决办法: Masm 中使用段前缀 mov ax,ds:[0]
      相当于: (ax)=((ds)*16+0))

    

[ Last edited by redtek on 2006-12-24 at 09:42 AM ]
作者: redtek     时间: 2006-12-24 22:46    标题: 【80x86汇编语言学习笔记】

安全内存空闲空间

  Quote:
在一般PC机中,DOS方式下,DOS和其他合法的程序一般都不会使用 0:200 ~~ 0:300(00200H~~00300H)的 256 个字节的空间。

所以,我们使用这段空间是安全的~:)



  Quote:
C:\WINDOWS>debug
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 3533D2        XOR     AX,D233
-D 0:200 300
0000:0200  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0210  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0230  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0240  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0250  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0260  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0270  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0280  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0290  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:02A0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:02B0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:02C0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:02D0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:02E0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:02F0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0300  00                                                .
-Q


作者: redtek     时间: 2006-12-24 23:06    标题: 【80x86汇编语言学习笔记】

段前缀的使用


理解将内存 ffff:0 ~~ ffff:b 单元中的数据拷贝到 0:200~0:20b 单元中的过程。
0:200~~0:20b = 200:0 ~~ 200:b

通过循环拷贝地址中的内容,要初始化偏移地址变量bx,赋值循环次数变量cx,循环12次(0~~b)。

例程

  Quote:
;====================================================
;
;将内存 ffff:0 ~~ ffff:b 单元中数据拷贝到0:200~0:20b单元中
;
;====================================================

              assume    cs:code

        code  segment

              mov       bx,0        ; 偏移地址清零(也是从0开始)
              mov       cx,12       ; 循环12次
          s:  mov       ax,0ffffh
              mov       ds,ax       ; 改变ds段地址
              mov       dl,[bx]
              mov       ax,0020h
              mov       ds,ax       ; 改变ds段地址,用于写内存
              mov       [bx],dl     ; 将dl中的数据送入 0020:bx 内存
              inc       bx          ; bx地址递增1
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end

执行后

  Quote:
C:\Masm50>debug p112.exe
-r
AX=0000  BX=0000  CX=001C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 BB0000        MOV     BX,0000
-d ffff:0 b
FFFF:0000  EA 5B E0 00 F0 30 34 2F-30 32 2F 30               .[...04/02/0
-

(看一下执行前这段内存中的内容)


-
-d 0020:0 b
0020:0000  00 00 00 00 00 00 00 00-00 00 00 00               ............

(看一下上面要复制到的目标内存中的内容-没有内容)

-g

Program terminated normally

(上面 g 运行这段复制内存的代码)

(看一下执行完代码后, 0020:0 ~~ 0020:b 这段内容是否被复制过来内容了)

-d 0020:0 b
0020:0000  EA 5B E0 00 F0 30 34 2F-30 32 2F 30               .[...04/02/0

(上面这段内存是从 ffff:0 ~~ ffff:b 这段内存所复制过来的)

关于上面代码循环的改进过程……


每循环都要设置改变两次ds段地址,但效率不高。
使用两个段寄存器分别存放单元 ffff:x 和目标单元 0020:x 的段地址,效率上会提高一些。

改进例程

  Quote:
              assume    cs:code

        code  segment

              mov       ax,0ffffh
              mov       ds,ax
              mov       ax,0020h
              mov       es,ax

              mov       bx,0

              mov       cx,12
          s:  mov       dl,[bx]
              mov       es:[bx],dl
              inc       bx
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end

上面蓝色标注是段前缀的使用,强制指定是哪一个段。


Debug 跟踪调试结果

  Quote:
C:\Masm50>debug p112-59.exe
-r
AX=0000  BX=0000  CX=001D  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 B8FFFF        MOV     AX,FFFF
-d ffff:0 b
FFFF:0000  EA 5B E0 00 F0 30 34 2F-30 32 2F 30               .[...04/02/0
-
-d 20:0 b
0020:0000  00 00 00 00 00 00 00 00-00 00 00 00               ............-
-g

Program terminated normally
-
-d 20:0 b
0020:0000  EA 5B E0 00 F0 30 34 2F-30 32 2F 30               .[...04/02/0-
-q


实验:


1) 编程,向内存 0:200~~0:23F 依次传送数据 0~63(3FH)。
   程序中只能使用 9 条指令。

第一次的代码写内存的顺序反了,哈哈……
-d 0:200 23f
0000:0200  3F 3E 3D 3C 3B 3A 39 38-37 36 35 34 33 32 31 30   ?>=<;:9876543210
0000:0210  2F 2E 2D 2C 2B 2A 29 28-27 26 25 24 23 22 21 20   /.-,+*)('&%$#"!
0000:0220  1F 1E 1D 1C 1B 1A 19 18-17 16 15 14 13 12 11 10   ................
0000:0230  0F 0E 0D 0C 0B 0A 09 08-07 06 05 04 03 02 01 00   ................

重来……


下面是重写的代码经过Debug调试后发现 0:200 地址处内容本该被写入 00H,结果并没有被写入。

经过Debug跟踪分析,原来我是用cx=3F经循环被Loop递减运算时,当Loop遇3F为0时,则退出了,所以这个00H没机会写入。

看来这种倒着写的方法有待改进:)

(下面是错误的内容)

  Quote:
-d 0:200 23f
0000:0200  3F 01 02 03 04 05 06 07-08 09 0A 0B 0C 0D 0E 0F   ?...............
0000:0210  10 11 12 13 14 15 16 17-18 19 1A 1B 1C 1D 1E 1F   ................
0000:0220  20 21 22 23 24 25 26 27-28 29 2A 2B 2C 2D 2E 2F    !"#$%&'()*+,-./
0000:0230  30 31 32 33 34 35 36 37-38 39 3A 3B 3C 3D 3E 3F   0123456789:;<=>?

调试正确的代码:

  Quote:
;--------------------------------------------------
;    向内存 0:200~~0:23F 依次传送数据 0~63(3FH)
;--------------------------------------------------

              assume    cs:code
        code  segment

              mov       ax,20h
              mov       ds,ax
              mov       bx,0h       ; 设置偏移量

              mov       cx,64
          s:  mov       ds:[bx],bl
              inc       bx
              loop      s
              mov       ax,4c00h
              int       21h

        code  ends
              end

调试成功~:)

  Quote:
-d 0:200 23f
0000:0200  00 01 02 03 04 05 06 07-08 09 0A 0B 0C 0D 0E 0F   ................
0000:0210  10 11 12 13 14 15 16 17-18 19 1A 1B 1C 1D 1E 1F   ................
0000:0220  20 21 22 23 24 25 26 27-28 29 2A 2B 2C 2D 2E 2F    !"#$%&'()*+,-./
0000:0230  30 31 32 33 34 35 36 37-38 39 3A 3B 3C 3D 3E 3F   0123456789:;<=>?

如果没有 Debug 调试这个编译的 .exe 文件,根本没有办法直接发现程序中的问题。

越来越感觉到 Debug 就象是用于看到代程序过程并方便调试代码用的(批处理中的) Echo on 这样的命令~:)

而且,没有象 Debug 类似或是一样的工具帮助调试已编译了的代码,将寸步难行~:)


上面代码原理: 从偏移量 0~3F 一共有 64 个字节(因为偏移量0也算一个,刚才给忘了)
        所以设置循环 64 次。
        将 0:200 (段:偏移量)地址设置为 200:0000 地址( (段*16+偏移量) / 16 :0000 ),方便计算。
        所以 bx 寄存器中的偏移量就从 0000 开始计数了~:)
        只要计数累加偏移量到 64 时,那个cx计数器中的循环64次也正好结束,所有指定内存全都写入了。

[ Last edited by redtek on 2006-12-24 at 12:33 PM ]
作者: redtek     时间: 2006-12-25 01:35    标题: 【80x86汇编语言学习笔记】

关于 Debug 的一个调试技巧

  Quote:
C:\Masm50>debug p113.exe
-r
AX=0000  BX=0000  CX=0015  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 B82000        MOV     AX,0020
-u
0B49:0000 B82000        MOV     AX,0020
0B49:0003 8ED8          MOV     DS,AX
0B49:0005 BB0000        MOV     BX,0000
0B49:0008 B94000        MOV     CX,0040
0B49:000B 881F          MOV     [BX],BL
0B49:000D 43            INC     BX
0B49:000E E2FB          LOOP    000B


0B49:0010 B8004C        MOV     AX,4C00
0B49:0013 CD21          INT     21
0B49:0015 05508D        ADD     AX,8D50
0B49:0018 46            INC     SI
0B49:0019 8050E83E      ADC     BYTE PTR [BX+SI-18],3E
0B49:001D 0D83C4        OR      AX,C483
-
-
-g 0010

AX=0020  BX=0040  CX=0000  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0020  ES=0B39  SS=0B49  CS=0B49  IP=0010   NV UP EI PL NZ AC PO NC
0B49:0010 B8004C        MOV     AX,4C00
-q

假如不再想跟踪调试上面绿色部分的代码了,想一下子执行完绿色部分的代码,
但只让程序执行到红色标注的代码处停止。

则可以用 g 命令加参数执行:

  G  0010  

含义:从当前 CS:IP 指针处执行代码,执行到标号 0010 处停止。

[ Last edited by redtek on 2006-12-24 at 12:40 PM ]
作者: redtek     时间: 2006-12-25 02:27    标题: 【80x86汇编语言学习笔记】

代码复制自身到另一块内存



) 下面的程序的功能是将 "mov ax,4c00h" 之前的指令复制到内存 0:200 处,补全程序。
  上机调试,跟踪运行结果。
;----------------------------------------
;
;             未完全的代码,待填补完
;
;----------------------------------------

              assume    cs:code

        code  segment

              mov       ax,
              mov       ds,ax
              mov       ax,0020h
              mov       es,ax
              mov       bx,0
              mov       cx,
          s:  mov       al,[bx]
              mov       es:[bx],al
              inc       bx
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end
根据以上代码思考问题:

1) 复制的是什么?从哪里到哪里?
2) 复制的是什么?有多少个?如何知道要复制的字节的数量?

根据上面代码,分析如下:

  (ds)*16+256B= cs 所以 cs =(ds)+10H
  
  复制的代码不能包括 “mov  ax,4c00h” 与 “int  21h” 这两句,如何知道其长度?
  反汇编这两句代码信息如下:
  
  B8004C        MOV     AX,4C00
  CD21           INT     21
  
  其中:B8004C与CD21是上面 mov ……命令的机器码,数一下。机器码是:5个字节。

  所以,(代码总长度 - 5B )=要复制的代码长度。
  即: CX-5=要复制的但又不含上面两句指令的代码总长度。

  所以,循环次数是 cs-5 。cs中具体是多少要Debug调入才知道(或是根据经验数),不过咱们把循环次数瞎添一个就知道了。


下面是补全代码以后,唯一不知道总长度,也不知道要循环多少次。
所以,用汇编代码中的cx循环次数21是瞎写的,先占个长度,这样先能编译过去再说。

然后到了Debug中所看到的红色标注部分就是代码的总长度,我们再用总长度减那两条语句的机器码长度5字节,就是cx循环次数。

  Quote:
C:\Masm50>debug p113-1.exe
-r
AX=0000  BX=0000  CX=001C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 8BC1          MOV     AX,CS
-u
0B49:0000 8BC1          MOV     AX,CS
0B49:0002 8ED8          MOV     DS,AX
0B49:0004 B82000        MOV     AX,0020
0B49:0007 8EC0          MOV     ES,AX
0B49:0009 BB0000        MOV     BX,0000
0B49:000C B91500        MOV     CX,0015
0B49:000F 8A07          MOV     AL,[BX]
0B49:0011 26            ES:
0B49:0012 8807          MOV     [BX],AL
0B49:0014 43            INC     BX
0B49:0015 E2F8          LOOP    000F
0B49:0017 B8004C        MOV     AX,4C00
0B49:001A CD21          INT     21
0B49:001C 3E            DS:
0B49:001D 0D83C4        OR      AX,C483
-q

长度为 0x001c ,再减那两条不包括的指令的5个字节,为 23 。即直要复制的代码长度为 23 个字节~:)
C:\Masm50>set /a 0x001c-5
23
我是用计算器算的,如果觉得用计算器算太麻烦,还可以向上面一样秀一下DOS命令~:)同样支持16进制与10进制混合运算~:)


补全后的代码

;----------------------------------------
;
;            补全后的代码,复制代码自身到内存0:200处
;
;----------------------------------------

              assume    cs:code

        code  segment

              mov       ax,cs
              mov       ds,ax
              mov       ax,0020h
              mov       es,ax
              mov       bx,0
              mov       cx,23
          s:  mov       al,[bx]
              mov       es:[bx],al
              inc       bx
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end
Debug跟踪过程,验证是否将自身代码复制到 0:200 处

下面是反汇编代码,方便参考。

  Quote:
C:\Masm50>debug p113-1.exe
-r
AX=0000  BX=0000  CX=001C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 8CC8          MOV     AX,CS
-u
0B49:0000 8CC8          MOV     AX,CS
0B49:0002 8ED8          MOV     DS,AX
0B49:0004 B82000        MOV     AX,0020
0B49:0007 8EC0          MOV     ES,AX
0B49:0009 BB0000        MOV     BX,0000
0B49:000C B91700        MOV     CX,0017
0B49:000F 8A07          MOV     AL,[BX]
0B49:0011 26            ES:
0B49:0012 8807          MOV     [BX],AL
0B49:0014 43            INC     BX
0B49:0015 E2F8          LOOP    000F
0B49:0017 B8004C        MOV     AX,4C00
0B49:001A CD21          INT     21

下面是运行代码前的目标内存状态与运行代码后的目标内存状态

  Quote:
C:\Masm50>debug p113-1.exe
-r
AX=0000  BX=0000  CX=001C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 8CC8          MOV     AX,CS
-d 0:200
0000:0200  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0210  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0230  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0240  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0250  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0260  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0270  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
-g

Program terminated normally
-d 0:200
0000:0200  8C C8 8E D8 B8 20 00 8E-C0 BB 00 00 B9 17 00 8A   ..... ..........
0000:0210  07 26 88 07 43 E2 F8 00-00 00 00 00 00 00 00 00   .&..C...........
0000:0220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0230  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0240  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0250  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0260  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0270  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-q

  
下面是执行后的目标内存情况(被拷了过去)

  Quote:
-d 0:200
0000:0200  8C C8 8E D8 B8 20 00 8E-C0 BB 00 00 B9 17 00 8A   ..... ..........
0000:0210  07 26 88 07 43 E2 F8 00-00 00 00 00 00 00 00 00   .&..C...........
0000:0220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0230  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0240  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0250  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0260  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0270  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

反汇编被拷贝过去的目标内存

  Quote:
-u 0:200
0000:0200 8CC8          MOV     AX,CS
0000:0202 8ED8          MOV     DS,AX
0000:0204 B82000        MOV     AX,0020
0000:0207 8EC0          MOV     ES,AX
0000:0209 BB0000        MOV     BX,0000
0000:020C B91700        MOV     CX,0017
0000:020F 8A07          MOV     AL,[BX]
0000:0211 26            ES:
0000:0212 8807          MOV     [BX],AL
0000:0214 43            INC     BX
0000:0215 E2F8          LOOP    020F

看一下主代码(进行复制的主代码)

  Quote:
C:\Masm50>debug p113-1.exe
-r
AX=0000  BX=0000  CX=001C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 8CC8          MOV     AX,CS
-u
0B49:0000 8CC8          MOV     AX,CS
0B49:0002 8ED8          MOV     DS,AX
0B49:0004 B82000        MOV     AX,0020
0B49:0007 8EC0          MOV     ES,AX
0B49:0009 BB0000        MOV     BX,0000
0B49:000C B91700        MOV     CX,0017
0B49:000F 8A07          MOV     AL,[BX]
0B49:0011 26            ES:
0B49:0012 8807          MOV     [BX],AL
0B49:0014 43            INC     BX
0B49:0015 E2F8          LOOP    000F

[ Last edited by redtek on 2006-12-24 at 02:56 PM ]
作者: redtek     时间: 2006-12-25 05:28    标题: 【80x86汇编语言学习笔记】

包含多个段的程序

程序取得所需空间的方法:

1) 加载程序的时候为程序分配
2) 程序在执行的过程中向系统申请


dw,字型数据。


在代码中使用数据例程
              assume    cs:code
        code  segment

              dw        0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
              mov       bx,0        ; 偏移量
              mov       ax,0
              mov       cx,8        ; 循环8次

          s:  add       ax,cs:[bx]  ; (ax)= ((ax)+(cs)*16+(bx))
              add       bx,2
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end
Debug 跟踪上面编译了的代码



反汇编看到其 dw 所定义的数据被定义到了代码段首(因为dw是第一条指令),其内容如下:

  Quote:
-d cs:0000 f
0B49:0000  23 01 56 04 89 07 BC 0A-EF 0D ED 0F BA 0C 87 09   #.V.............

但是,上面的代码会出现问题,因为前16个字节不是代码,而是我们定义的数据。


Debug 分析上面被编译之后的代码

  Quote:
C:\Masm50>debug p115.exe
-r
AX=0000  BX=0000  CX=0026  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 2301          AND     AX,[BX+DI]                         DS:0000=20CD
-u
0B49:0000 2301          AND     AX,[BX+DI]
0B49:0002 56            PUSH    SI
0B49:0003 0489          ADD     AL,89
0B49:0005 07            POP     ES
0B49:0006 BC0AEF        MOV     SP,EF0A
0B49:0009 0DED0F        OR      AX,0FED
0B49:000C BA0C87        MOV     DX,870C
0B49:000F 09BB0000      OR      [BP+DI+0000],DI
0B49:0013 B80000        MOV     AX,0000
0B49:0016 B90800        MOV     CX,0008
0B49:0019 2E            CS:
0B49:001A 0307          ADD     AX,[BX]
0B49:001C 83C302        ADD     BX,+02
0B49:001F E2F8          LOOP    0019

(上面列出的前16个字节是我们定义的数据,不是指令)


(下面才是我们定义的指令,但这批令应该在程序的入口: cs:ip )
(所以,有可能会出错)

-u cs:0010
0B49:0010 BB0000        MOV     BX,0000
0B49:0013 B80000        MOV     AX,0000
0B49:0016 B90800        MOV     CX,0008
0B49:0019 2E            CS:
0B49:001A 0307          ADD     AX,[BX]
0B49:001C 83C302        ADD     BX,+02
0B49:001F E2F8          LOOP    0019
0B49:0021 B8004C        MOV     AX,4C00
0B49:0024 CD21          INT     21
0B49:0026 00EB          ADD     BL,CH
0B49:0028 0590FF        ADD     AX,FF90
0B49:002B 86FE          XCHG    BH,DH
0B49:002D FEA15607      JMP     [BX+DI+0756]
-q

在源程序中指明程序入口所在
              assume    cs:code
        code  segment

              dw        0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

      start:  mov       bx,0
              mov       ax,0
          s:  add       ax,cs:[bx]
              add       bx,2
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end       start
Debug 跟踪上面编译了的代码
C:\Masm50>debug p116.exe
-r
AX=0000  BX=0000  CX=0023  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0010   NV UP EI PL NZ NA PO NC
0B49:0010 BB0000        MOV     BX,0000
-u
0B49:0010 BB0000        MOV     BX,0000
0B49:0013 B80000        MOV     AX,0000
0B49:0016 2E            CS:
0B49:0017 0307          ADD     AX,[BX]
0B49:0019 83C302        ADD     BX,+02
0B49:001C E2F8          LOOP    0016
0B49:001E B8004C        MOV     AX,4C00
0B49:0021 CD21          INT     21
0B49:0023 FEFE          ???     DH
0B49:0025 0000          ADD     [BX+SI],AL
0B49:0027 EB05          JMP     002E
0B49:0029 90            NOP
0B49:002A FF86FEFE      INC     WORD PTR [BP+FEFE]
0B49:002E A15607        MOV     AX,[0756]
-q
发现 ip 已指向 10  ,cpu执行时跳过了数据区。

嘿嘿……开始奇怪了,越奇怪思考起来越有味道~:)
病毒?修改程序入口?还能干什么?原由?

比较结果:一个带 end start  ,一个是 end ,比较入口不一样的地方
C:\Masm50\p>fc /b /a pok.exe pno.exe
正正在在比比较较文文件件 pok.exe 和和 PNO.EXE
00000012: AA BA
00000014: 10 00
改名比较

  Quote:
C:\Masm50\p>DEBUG POK.TXT
-D DS:100
0AF5:0100  4D 5A 23 00 02 00 00 00-20 00 00 00 FF FF 00 00   MZ#..... .......
0AF5:0110  00 00 AA 50 10 00 00 00-1E 00 00 00 01 00 00 00   ...P............
0AF5:0120  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0170  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-Q

C:\Masm50\p>DEBUG PNO.TXT
-R
AX=0000  BX=0000  CX=0223  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 4D            DEC     BP
-D DS:100
0AF5:0100  4D 5A 23 00 02 00 00 00-20 00 00 00 FF FF 00 00   MZ#..... .......
0AF5:0110  00 00 BA 50 00 00 00 00-1E 00 00 00 01 00 00 00   ...P............
0AF5:0120  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0170  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-Q

C:\Masm50\p>FC /B POK.TXT PNO.TXT
正正在在比比较较文文件件 pok.txt 和和 PNO.TXT
00000012: AA BA
00000014: 10 00

FC 执行比较的以后,将它的输出信息copy到这里居然显示“双影儿”字了,哈哈……

fc /b pok.txt pno.txt >test.txt之后,再用Debug重写一个新文件,再次16进制写这几个字,看看是不是重影儿?

打开记事这回是,copy 粘到这里。
正在比较文件 pok.txt 和
记事本内copy这内容看来没事儿,刚才重影是在 cmd 里直接copy 的……

type test.txt 一下,cmd 里copy ^
正正在在比比较较文文件件 pok.txt
好玩……

copy con a.txt 新建一个,直接打字看看 type 出来是什么……
正在比较文件 pok.tx
16进制一样,唯一区别就是 cmd 直接copy,刚一放到 [  code]里一粘就是双影,比较剪辑板?晕………………………………

会不会和 fc 在 cmd 中的输出方式有关?它用了另一种方式显示字符?
而这种方式显示的字符当 copy 到剪辑板时就变成了双字?
看来 echo 是达不到这种效果……
我看到的 fc 的输出和我copy出来的不同……
用这种方法进行防批处理运算结果输出中的防拷贝?

测试 fc 比较文件后,输出了一串中文字信息……
然后直接在cmd里鼠标右键选择copy……一粘结果还是双字……
奇怪………………

[ Last edited by redtek on 2006-12-24 at 10:49 PM ]
作者: electronixtar     时间: 2006-12-25 07:24
牛人,顶
作者: ccwan     时间: 2006-12-25 07:44
顶,人牛!
作者: redtek     时间: 2006-12-25 10:58
多谢两位仁兄鼓励:)
人牛?饶了我吧,嘿嘿……应该一边看着汇编一边咬着牛肉干儿~:)`````
作者: redtek     时间: 2006-12-25 22:03    标题: 【80x86汇编语言学习笔记】

数据、代码、栈在不同的段中使用


是圣诞节?时间流逝,冲刺……


1) 代码、数据、栈放到同一个段中会使程序混乱,难以维护。
2) 如果数据、栈、代码需要的空间超过64KB,就不能放在一个段中。
   不同的安排放到不同意义标识的段中,更使代码清晰容易扩展。

将数据、代码、栈放到不同的段中,例程分析:



) 在定义 stack 栈段的代码中,编译器编译给出 "no  stack  segment" 警告,在 Google.com 查询到的解决办法:

  问: 已在程序中设有如下定义:STACK  SEGMENT ……为什么LINK 仍然警告:no  stack  segment ?

  答:将段定义修改为:STACK  SEGMENT  STACK ;后面的STACK项是组合类型,即不再出现警告信息。


多段方式的代码
              assume    cs:code,ds:data,ss:stack
        data  segment
              dw        0123h,0456h,0789h,0abch,,0defh,0fedh,0cbah,0987h
        data  ends
       stack  segment
              dw        0,0,0,0,0,0,0,0
       stack  ends
        code  segment
      start:  mov       ax,stack    ; start入口
              mov       ss,ax
              mov       sp,16
              mov       ax,data
              mov       ds,ax
              mov       bx,0
              mov       cx,8
          s:  push      [bx]
              add       bx,2
              loop      s
              mov       bx,0
              mov       cx,8
         s0:  pop       [bx]
              add       bx,2
              loop      s0
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
编译可执行文件后用Debug调试,发现一个错误~:)
发现多了个 00H 00H ,源代码中定义的数据什么时候定义了这个东东?
后来一数,是多了一个数据 00H 00H ,原来是写源代码的时候多加一个逗号,编译器认为这是合法的一个数据 00 00 。

  Quote:
-d ds:0000
0B49:0000  23 01 56 04 89 07 BC 0A-00 00 EF 0D ED 0F BA 0C   #.V.............
0B49:0010  87 09 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0B49:0020  00 00 13 00 4C 0B 59 05-BC 0A 89 07 56 04 23 01   ....L.Y.....V.#.
0B49:0030  B8 4B 0B 8E D0 BC 10 00-B8 49 0B 8E D8 BB 00 00   .K.......I......
0B49:0040  B9 08 00 FF 37 83 C3 02-E2 F9 BB 00 00 B9 08 00   ....7...........
0B49:0050  8F 07 83 C3 02 E2 F9 B8-00 4C CD 21 FE 89 96 FC   .........L.!....
0B49:0060  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B49:0070  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P

绿色标注相当于源代码中的:
        data  segment
              dw        0123h,0456h,0789h,0abch,,0defh,0fedh,0cbah,0987h
        data  ends
红色标记是源代码中多打了个逗号所占位的。

蓝色标记为栈空间预留,相于源代中的
       stack  segment
              dw        0,0,0,0,0,0,0,0
       stack  ends
然后再 Debug 调试,紧接着就是这个有意思的错误。
但如果在 MS-DOS 下调试则没有问题,因为栈并没有越界,它就算是“指针”越界,但数据不会越界(数据长度刚刚好)。
可见(个人目前认为) CMD 太严格了!

[ Last edited by redtek on 2006-12-25 at 11:19 AM ]
附件 1: 1.GIF (2006-12-26 00:19, 14.91 K, 下载附件所需积分 1点 ,下载次数: 2)



作者: redtek     时间: 2006-12-26 01:12    标题: 【80x86汇编语言学习笔记】

编写调试跟踪多段程序

一个小窍门

刚才希望截取 Debug 操作的过程文字,结果内容过长,无法 copy 文件。
试着Debug输出内容重向到一个文本文件内,好是好,只是生成了临时文件。

想了想发现一个好玩的办法,不生成临时文件,直接将操作过程管道输出到剪辑板内,然后我往论坛一粘就行了~:)

不过,所输出的内容是不会显示到屏幕上的。但是用熟了也用着显示什么,知道自己键入的是什么应该出现些什么就行了。
debug p123.exe|clip
下面源代码编译后调试跟踪过程
              assume    cs:code,ds:data,ss:stack
        data  segment
              dw        0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
        data  ends

       stack  segment
              dw        0,0,0,0,0,0,0,0
       stack  ends

        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       ax,data
              mov       ds,ax
              push      ds:[0]
              push      ds:[2]
              pop       ds:[2]
              pop       ds:[0]
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
试调上面编译过的源代码

  Quote:
-r

AX=0000  BX=0000  CX=003F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000  
DS=0B39  ES=0B39  SS=0B49  CS=0B4B  IP=0000   NV UP EI PL NZ NA PO NC
0B4B:0000 B84A0B        MOV        AX,0B4A                           
-u

0B4B:0000 B84A0B        MOV        AX,0B4A                           
0B4B:0003 8ED0          MOV        SS,AX                              
0B4B:0005 B8490B        MOV        AX,0B49                           
0B4B:0008 8ED8          MOV        DS,AX                              
0B4B:000A FF360000      PUSH        [0000]                             
0B4B:000E FF360200      PUSH        [0002]                             
0B4B:0012 8F060200      POP        [0002]                             
0B4B:0016 8F060000      POP        [0000]                             
0B4B:001A B8004C        MOV        AX,4C00                           
0B4B:001D CD21          INT        21                                 
0B4B:001F 8B87BE22      MOV        AX,[BX+22BE]                       
-d cs:0

0B4B:0000  B8 4A 0B 8E D0 B8 49 0B-8E D8 FF 36 00 00 FF 36   .J....I....6...6
0B4B:0010  02 00 8F 06 02 00 8F 06-00 00 B8 00 4C CD 21 8B   ............L.!.
0B4B:0020  87 BE 22 0B 87 C0 22 74-E1 8B 9E FE FE D1 E3 D1   .."..."t........
0B4B:0030  E3 8B 87 BE 22 8B 97 C0-22 89 86 FA FE 89 96 FC   ...."...".......
0B4B:0040  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B4B:0050  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P
0B4B:0060  E8 6F 70 83 C4 06 B8 CD-05 50 8D 86 00 FF 50 E8   .op......P....P.
0B4B:0070  CA 0C 83 C4 04 B8 FF FF-50 8D 86 00 FF 50 8D 46   ........P....P.F
-d ss:0

0B49:0000  23 01 56 04 89 07 BC 0A-EF 0D ED 0F BA 0C 87 09   #.V.............
0B49:0010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0B49:0020  B8 4A 0B 8E D0 B8 49 0B-8E D8 FF 36 00 00 FF 36   .J....I....6...6
0B49:0030  02 00 8F 06 02 00 8F 06-00 00 B8 00 4C CD 21 8B   ............L.!.
0B49:0040  87 BE 22 0B 87 C0 22 74-E1 8B 9E FE FE D1 E3 D1   .."..."t........
0B49:0050  E3 8B 87 BE 22 8B 97 C0-22 89 86 FA FE 89 96 FC   ...."...".......
0B49:0060  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B49:0070  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P
-d ds:0

0B39:0000  CD 20 FF 9F 00 9A F0 FE-1D F0 4F 03 59 05 8A 03   . ........O.Y...
0B39:0010  59 05 17 03 59 05 48 05-01 03 01 00 02 FF FF FF   Y...Y.H.........
0B39:0020  FF FF FF FF FF FF FF FF-FF FF FF FF 06 0B 4C 01   ..............L.
0B39:0030  19 0A 14 00 18 00 39 0B-FF FF FF FF 00 00 00 00   ......9.........
0B39:0040  05 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0B39:0050  CD 21 CB 00 00 00 00 00-00 00 00 00 00 20 20 20   .!...........   
0B39:0060  20 20 20 20 20 20 20 20-00 00 00 00 00 20 20 20           .....   
0B39:0070  20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00           ........
-q

代码装入执行前与执行改变栈地址之后的数据对比

  Quote:
-R
AX=0000  BX=0000  CX=003F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4B  IP=0000   NV UP EI PL NZ NA PO NC
0B4B:0000 B84A0B        MOV     AX,0B4A
-T

AX=0B4A  BX=0000  CX=003F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4B  IP=0003   NV UP EI PL NZ NA PO NC
0B4B:0003 8ED0          MOV     SS,AX
-T

AX=0B49  BX=0000  CX=003F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B4A  CS=0B4B  IP=0008   NV UP EI PL NZ NA PO NC
0B4B:0008 8ED8          MOV     DS,AX
-D SS:0
0B4A:0000  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0B4A:0010  B8 4A 0B 8E D0 B8 49 0B-8E D8 FF 36 00 00 FF 36   .J....I....6...6
0B4A:0020  02 00 8F 06 02 00 8F 06-00 00 B8 00 4C CD 21 8B   ............L.!.
0B4A:0030  87 BE 22 0B 87 C0 22 74-E1 8B 9E FE FE D1 E3 D1   .."..."t........
0B4A:0040  E3 8B 87 BE 22 8B 97 C0-22 89 86 FA FE 89 96 FC   ...."...".......
0B4A:0050  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B4A:0060  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P
0B4A:0070  E8 6F 70 83 C4 06 B8 CD-05 50 8D 86 00 FF 50 E8   .op......P....P.
-D 0B49:0
0B49:0000  23 01 56 04 89 07 BC 0A-EF 0D ED 0F BA 0C 87 09   #.V.............
0B49:0010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0B49:0020  B8 4A 0B 8E D0 B8 49 0B-8E D8 FF 36 00 00 FF 36   .J....I....6...6
0B49:0030  02 00 8F 06 02 00 8F 06-00 00 B8 00 4C CD 21 8B   ............L.!.
0B49:0040  87 BE 22 0B 87 C0 22 74-E1 8B 9E FE FE D1 E3 D1   .."..."t........
0B49:0050  E3 8B 87 BE 22 8B 97 C0-22 89 86 FA FE 89 96 FC   ...."...".......
0B49:0060  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B49:0070  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P

上面是代码装入执行前和改变栈ds的对比,根据上面对比再对比整个代码使用内存的变化。

  Quote:
C:\Masm50>debug p123.exe
-r
AX=0000  BX=0000  CX=003F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4B  IP=0000   NV UP EI PL NZ NA PO NC
0B4B:0000 B84A0B        MOV     AX,0B4A
-d ss:0
0B49:0000  23 01 56 04 89 07 BC 0A-EF 0D ED 0F BA 0C 87 09   #.V.............
0B49:0010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0B49:0020  B8 4A 0B 8E D0 B8 49 0B-8E D8 FF 36 00 00 FF 36   .J....I....6...6
0B49:0030  02 00 8F 06 02 00 8F 06-00 00 B8 00 4C CD 21 C7   ............L.!.
0B49:0040  02 75 48 89 3E E6 99 FF-06 E6 99 C6 06 E8 99 FF   .uH.>...........
0B49:0050  C6 06 E9 99 00 E8 99 00-AC E8 29 E2 74 38 3C 0D   ..........).t8<.
0B49:0060  74 34 3A 06 B6 96 74 2E-3A C3 74 2A 3C 3A 74 03   t4:...t.:.t*<:t.
0B49:0070  E9 5F FF 80 3E A4 98 02-75 05 E8 74 00 EB D9 46   ._..>...u..t...F


红色标注为 ss 栈段在代码执行前未重新分配时的状态,ss 基地址批向数据在源代码中的定义:

        data  segment
              dw        0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
        data  ends
而紧跟着它后面的数据 00 00  ……则是为栈安排存储区,它在代码中是这样定义的
       stack  segment
              dw        0,0,0,0,0,0,0,0
       stack  ends
上面的定义这才是应该为栈分配的空间。

而在这一片 “内存区域” 的后面才是代码入口 ( end start ),cs段地址为:0B4BH

执行前:
|______PSP____|____ss=0b49=8个dw定义的字数据___后8个字00为栈分配___共32字节__|__代码段:  cs=0b4b__|
执行后(只执行  为ss栈赋值后),则变化如下:

其 ss=B4A0 ,被定义到了原来的栈与代码段之间了(即向下移了16个字节),
由原来装入时的 SS=0B49 向下移了 16字节。
变为: SS=B4A0。

那么 SS=B4A0 它将指向哪里?见上面反汇编分析,其指向了 dw 0,0,0……在源代码中所定义的栈区。

这是为了方便压栈用。

分析:

) CPU执行程序,程序返回前,data 段中的数据为多少?

  
  -d ds:0
  0B49:0000  23 01 56 04 89 07 BC 0A-EF 0D ED 0F BA 0C 87 09
  
) CPU执行程序,程序返回前,CS=? SS=? DS=?

  CS=0B4B  SS=0B4A  DS=0B49


) 设程序加载后,code段的段地址为X,则data段的段地址为?
  stack段的段地址为?


  stack段的段地址为: stack=(cs)*16+0B-32B
  data段的段地址为:   data=(cs)*16+0B-32B - 100H



编译下面代码,用Debug加载、跟踪、调试……
              assume    cs:code,ds:data,ss:stack

        data  segment
              dw        0123h,0456h
        data  ends

       stack  segment
              dw        0,0
       stack  ends

        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,16
              mov       ax,data
              mov       ds,ax
              push      ds:[0]
              push      ds:[2]
              pop       ds:[2]
              pop       ds:[0]
              mov       ax,4c00h
              int       21h

        code  ends
              end       start
) CPU执行程序,程序返回前,data段中的数据为多少?
  
  DS = 0B49
  DS = 23 01 56 04


) CPU执行程序,程序返回前, CS、SS、DS都是多少?

  DS=0B49  SS=0B4A  CS=0B4B


) 设程序加载后,code段的段地址为X,则data段的段地址为?
  stack段的段地址为?

  stack段的段地址为: ( (X)*16 - 32B) / 16
  data 段的段地址为: ( (X)*16 - 32B - 256B) ) / 16
  


) 对于如下定义的段:

  name segment
    ...
  name ends

  如果段中的数据占N个字节,则程序加载后,该段实际占有空间为?

  答:如果 N < FH ,则实际占用 FH 字节。



简单总结:

(到这里,发现一个例子反复的跟踪和对比源代码被编译并反汇编后的“样子”是绝对值得的,直到脑子里已经形成视图为止。)

(每一个基础如果不打好,就向下走,会学得非常快,但越向后学难度越大、难以逾越的障碍就会越多,能力难以再提高。)

(汇编离不开Debug等调试工具,没了这些工具寸步难行。汇编学机器原理,Debug了解真象。)



将下面的程序编译连接,则Debug加载、跟踪……
              assume    cs:code,ds:data,ss:stack
        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,16
              mov       ax,data
              mov       ds,ax
              push      ds:[0]
              push      ds:[2]
              pop       ds:[2]
              pop       ds:[0]
              mov       ax,4c00h
              int       21h
        code  ends
        data  segment
              dw        0123h,0456h
        data  ends
       stack  segment
              dw        0,0
       stack  ends
              end       start

分析:


  {PSP}{代码}{数据}{栈}




) CPU执行程序,程序返回前,data段中的数据为多少?
  
  DS=0B4C
  0B4C:0000  23 01 56 04


) CPU执行程序,程序返回前,CS=? SS=? DS=?

  SS=0B4D  CS=0B49 DS=0B4C


) 设程序加载后,code段的段地址为X,则data段的段地址为?
  stack段的段地址为?


  因为 code 段之前没  stack、data 等数据,只有 psp所占用的256B。
  所以 data 段地址为: ((CS)*16 - 256B )/ 16
  公式写得不太标准。因为文件一被装入,data段自然指向PSP,而stack与data段都在code段后面,
  所以code段地址(CS)向回移255B就是PSP。

  CX=44H 即:寄存器代码长度为:68 B
  实际手工数代码长度为:34 B
  则 68B - 34B = 34 B  则视图如下:{有效代码占34B+补满F}{data+补满到F + stack实际占用}
  (上面只是视图,不是真正计算方法)
  (因为代码结束后的空余空间在F以前是不再用了,为了整除方便)
  (所以,再有数据,如栈和数据段的超始地址都是可以将来被16整除的地址,这是最重要的一点)


  即: CX代段总长度 - (DATA: dw 0123h,0456h 独占的16倍数的起始行 = 16字节) == DATA段基地址
     (CX=68B)-(DATA: 16B)-(最后的stack所真实占用的4B)= 48B(CS段的偏移量)
     (CS=0B49H)+48B =0B4CH (这是求出的段地址)

  *  现在我们求出了已知CS地址,所以DATA段的地址为: 0B4CH
     那么假如 CS=X ,则DATA段地址为: ((x)*16+48B)/16
  
  *  既然已知DATA段地址了,那么STACK只不过是它向下的一个F篇移量,则STACK= (((x)*16+48B)/16)+16B


下面是一些求得如下公式的推理验证。

-d ss:0
0B49:0000  B8 4D 0B 8E D0 BC 10 00-B8 4C 0B 8E D8 FF 36 00
0B49:0010  00 FF 36 02 00 8F 06 02-00 8F 06 00 00 B8 00 4C
0B49:0020  CD 21 00 00 00 00 00 00-00 00 00 00 00 00 00 00

(上面 CD 21 就是代码结束了)


(下面 23 01 56 04 是 dw 0123H,0456H 所定义的数据。)
0B49:0030  23 01 56 04 00 00 00 00-00 00 00 00 00 00 00 00
(下面 00 00 00 是 stack 段在源代码中 dw 0,0 定义的栈空间)
(它们的特点:都是新起一行:)
0B49:0040  00 00 00 00 87 C0 22 74-E1 8B 9E FE FE D1 E3 D1
0B49:0050  E3 8B 87 BE 22 8B 97 C0-22 89 86 FA FE 89 96 FC
0B49:0060  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05
0B49:0070  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50
(它们为什么都要新起一行来存有被定义的新数据?)
(个人理解:)

(这是带有偏移量的数据,如果计算为无偏移量物理地址为:0B4E:1)
(当  MOV AX,0B4E:1 显然无法赋值)
(但 MOV AX,B4E1  表面上看上去是物理地址,但它不是相对段地址,所以更是出错)
(所以,偏移量只要的后面有0,方便段的物理地址(段)*16+偏移量,它的物理地址低位是0,方便再转为无偏移量

的段地址)
-d 0b4d:11
0B4D:0010     00 15 00 49 0B 59 05-56 04 23 01 FE 89 96 FC
0B4D:0020  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05
0B4D:0030  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50
0B4D:0040  E8 6F 70 83 C4 06 B8 CD-05 50 8D 86 00 FF 50 E8
0B4D:0050  CA 0C 83 C4 04 B8 FF FF-50 8D 86 00 FF 50 8D 46
0B4D:0060  80 50 E8 4D FA 83 C4 06-0A C0 75 03 E9 7B FF 5E
0B4D:0070  8B E5 5D C3 83 3E 56 07-20 72 0A B8 1C 04 50 E8
0B4D:0080  62 44 83 C4 02 B8 FF FF-50 B8 05 00 50 8D 86 7A
0B4D:0090  FE

-d 0b4e:1
0B4E:0000     00 15 00 49 0B 59 05-56 04 23 01 FE 89 96 FC
0B4E:0010  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05
0B4E:0020  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50
0B4E:0030  E8 6F 70 83 C4 06 B8 CD-05 50 8D 86 00 FF 50 E8
0B4E:0040  CA 0C 83 C4 04 B8 FF FF-50 8D 86 00 FF 50 8D 46
0B4E:0050  80 50 E8 4D FA 83 C4 06-0A C0 75 03 E9 7B FF 5E
0B4E:0060  8B E5 5D C3 83 3E 56 07-20 72 0A B8 1C 04 50 E8
0B4E:0070  62 44 83 C4 02 B8 FF FF-50 B8 05 00 50 8D 86 7A
0B4E:0080  FE
经过分析,反汇编代码 CD 21 (代码结束了)之后是 00 00 ……
因为刚才分析出必须是(段)*16+偏移量以后,其低位必须有0……
这样形容:就是当段地址*16+偏移量的物地址必须能被16整除才可以。




简单总结:

  上面的计算方法并不难,写了这么多笔记是为了推出它们的规律和都是为什么。背一个公式有记记力就行了,那么没有公式的时候怎么办?只能推出解决办法。要想找到解决办法就必须从源代码与反汇编的真实代码之间找到它们的规律,
  要找出规律就得用基础知识反复的验算。验算过程中就会发现很多不解的内容,如:为什么要空出一部分空间不用?为什么要新起一行才可以?这些书上没有,怎么办?一遍一遍推,一遍遍算,就会发现很多内容全是基础知识的扩充。



程序的入口地址分析

code segment
start: mov ax,stack ………………
        ………………
code ends
end start

如果没有在源代码中标出程序入口标置会怎么样?什么样的源代码未标置程序入口就不会运行?

分析如下:

  Quote:
              assume    cs:code,ds:data,ss:stack
        data  segment
              dw        0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
        data  ends

       stack  segment
              dw        0,0,0,0,0,0,0,0
       stack  ends

        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       ax,data
              mov       ds,ax
              push      ds:[0]
              push      ds:[2]
              pop       ds:[2]
              pop       ds:[0]
              mov       ax,4c00h
              int       21h
        code  ends
              end

编译后的可执行文件,代码入口就是源代码中按顺序放的数据,哈哈……无法运行~:)
从这里就可以看到 Start 与 end Start 的重要性了,它指出了代码结束与代码入口地址。不过start可以随便启名:)

  Quote:
C:\Masm50>debug aaa.exe
-r
AX=0000  BX=0000  CX=003F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B3B  ES=0B3B  SS=0B4B  CS=0B4B IP=0000   NV UP EI PL NZ NA PO NC
0B4B:0000 2301          AND     AX,[BX+DI]                         DS:0000=20CD
-u
0B4B:0000 2301          AND     AX,[BX+DI]
0B4B:0002 56            PUSH    SI
0B4B:0003 0489          ADD     AL,89
0B4B:0005 07            POP     ES
0B4B:0006 BC0AEF        MOV     SP,EF0A
0B4B:0009 0DED0F        OR      AX,0FED
0B4B:000C BA0C87        MOV     DX,870C
0B4B:000F 0900          OR      [BX+SI],AX
0B4B:0011 0000          ADD     [BX+SI],AL
0B4B:0013 0000          ADD     [BX+SI],AL
0B4B:0015 0000          ADD     [BX+SI],AL
0B4B:0017 0000          ADD     [BX+SI],AL
0B4B:0019 0000          ADD     [BX+SI],AL
0B4B:001B 0000          ADD     [BX+SI],AL
0B4B:001D 0000          ADD     [BX+SI],AL
0B4B:001F 00B84C0B      ADD     [BX+SI+0B4C],BH

) 将 A 段和 B 段中的数据依次相加,将结果 C 段中。 
              assume    cs:code
           a  segment
              db        1,2,3,4,5,6,7,8
           a  ends
           b  segment
              db        1,2,3,4,5,6,7,8
           b  ends
           c  segment
              db        0,0,0,0,0,0,0,0
           c  ends

        code  segment
      start:  mov       ax,a        ; A 段
              mov       ds,ax
              mov       dx,b        ; B 段
              mov       es,dx
              mov       bx,c        ; C 段
              mov       ss,bx

              mov       bx,0        ; 清零
              mov       cx,8

          s:  mov       al,ds:[bx]  ; 把 A 段数据送入 AL
              add       al,es:[bx]  ; 将 A 段数据与 B 段偏移量为BX的数据相加,结果送入AL
              mov       ss:[bx],al  ; 把最相加结果送入目标 S 段偏移量为bx处
              add       bx,1        ; “指针”递增 1
              loop      s

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
调试结果

  Quote:
-d ds:0
0B4B:0000  01 02 03 04 05 06 07 08-00 00 00 00 00 00 00 00   ................
0B4B:0010  01 02 03 04 05 06 07 08-00 00 00 00 00 00 00 00   ................
0B4B:0020  02 04 06 08 0A 0C 0E 10-00 00 00 00 00 00 00 00   ................
0B4B:0030  B8 4B 0B 8E D8 BA 4C 0B-8E C2 BB 4D 0B 8E D3 BB   .K....L....M....
0B4B:0040  00 00 B9 08 00 8A 07 26-02 07 36 88 07 83 C3 01   .......&..6.....
0B4B:0050  E2 F3 B8 00 4C CD 21 C0-22 89 86 FA FE 89 96 FC   ....L.!.".......
0B4B:0060  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B4B:0070  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P
-d es:0
0B4C:0000  01 02 03 04 05 06 07 08-00 00 00 00 00 00 00 00   ................
0B4C:0010  02 04 06 08 0A 0C 0E 10-00 00 00 00 00 00 00 00   ................
0B4C:0020  B8 4B 0B 8E D8 BA 4C 0B-8E C2 BB 4D 0B 8E D3 BB   .K....L....M....
0B4C:0030  00 00 B9 08 00 8A 07 26-02 07 36 88 07 83 C3 01   .......&..6.....
0B4C:0040  E2 F3 B8 00 4C CD 21 C0-22 89 86 FA FE 89 96 FC   ....L.!.".......
0B4C:0050  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B4C:0060  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P
0B4C:0070  E8 6F 70 83 C4 06 B8 CD-05 50 8D 86 00 FF 50 E8   .op......P....P.
-d ss:0
0B4D:0000  02 04 06 08 0A 0C 0E 10-00 00 00 00 00 00 00 00   ................
0B4D:0010  B8 4B 0B 8E D8 BA 4C 0B-8E C2 BB 4D 0B 8E D3 BB   .K....L....M....
0B4D:0020  00 00 B9 08 00 8A 07 26-02 07 36 88 07 83 C3 01   .......&..6.....
0B4D:0030  E2 F3 B8 00 4C CD 21 C0-22 89 86 FA FE 89 96 FC   ....L.!.".......
0B4D:0040  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B4D:0050  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P
0B4D:0060  E8 6F 70 83 C4 06 B8 CD-05 50 8D 86 00 FF 50 E8   .op......P....P.
0B4D:0070  CA 0C 83 C4 04 B8 FF FF-50 8D 86 00 FF 50 8D 46   ........P....P.F

) 用 push 指令将 a 段中 word 数据逆序存储到 b 段中。


              assume    cs:code
           a  segment
              dw        1,2,3,4,5,6,7,8
           a  ends
           b  segment
              dw        0,0,0,0,0,0,0,0
           b  ends
        code  segment
         start:
              mov       ax,a
              mov       ds,ax       ; 定义数据段 A
              mov       ax,b
              mov       es,ax       ; 定义目标数据段 B
              mov       ax,0020h    ; 定义栈使用安全空间
              mov       ss,ax       ; 定义栈
              mov       sp,100
              mov       bx,0        ; 清零
              mov       cx,8
         s0:  push      ds:[bx]     ; 数据段偏移量为[bx]的数据压栈
              add       bx,2
              loop      s0
              mov       bx,0
              mov       cx,8
         s1:  pop       es:[bx]     ; 逆序弹出存到 B 段
              add       bx,2
              loop      s1
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
跟踪结果如下

  Quote:
-d es:0
0B4C:0000  08 00 07 00 06 00 05 00-04 00 03 00 02 00 01 00
0B4C:0010  B8 4B 0B 8E D8 B8 4C 0B-8E C0 B8 20 00 8E D0 BC
0B4C:0020  64 00 BB 00 00 B9 08 00-FF 37 83 C3 02 E2 F9 BB
0B4C:0030  00 00 B9 08 00 26 8F 07-83 C3 02 E2 F8 B8 00 4C
0B4C:0040  CD 21 87 BE 22 8B 97 C0-22 89 86 FA FE 89 96 FC
0B4C:0050  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05
0B4C:0060  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50
0B4C:0070  E8 6F 70 83 C4 06 B8 CD-05 50 8D 86 00 FF 50 E8

[ Last edited by redtek on 2006-12-26 at 11:21 PM ]
作者: redtek     时间: 2006-12-27 22:48    标题: 【80x86汇编语言学习笔记】

灵活定位内存地址


and和or指令


) and  指令
  都为1时为1,否则为0
  通过该指令可将操作对象的相应位设为0,其它位不变。

    设:  al,01100011B

    将 al 的第6位设为0: and al,10111111B
    将 al 的第7位设为0: and al,01111111B
    将 al 的第0位设为0: and al,11111110B


) or  指令
  都为1时为1,或有一个为1时也为1,否则为0
  通过该指令可将操作对象的相应位设为1,其它位不变。

    设:  mov al,01100011B

    将 al 的第6位设为1:   or al,01000000B
    将 al 的第7位设为1: or al,10000000B
    将 al 的第0位设为1: or al,00000001B




对比源代码定义数据与编译后的可执行文件

  Quote:
              assume    ds:data,cs:code
        data  segment
              db        'unIX'
              dw        1,2,3
              db        'foRK'
        data  ends
        code  segment
      start:
        code  ends
              end       start

见到可执行文件其上所定义的数据存放形式

  Quote:
-d ss:0
0B49:0000  75 6E 49 58 01 00 02 00-03 00 66 6F 52 4B E2 70   unIX......foRK.p
0B49:0010  83 C4 06 B8 C8 05 50 8D-46 80 50 E8 3E 0D 83 C4   ......P.F.P.>...
0B49:0020  04 C7 86 FE FE 00 00 EB-05 90 FF 86 FE FE A1 56   ...............V
0B49:0030  07 39 86 FE FE 73 7D 8B-9E FE FE D1 E3 D1 E3 8B   .9...s}.........
0B49:0040  87 BE 22 0B 87 C0 22 74-E1 8B 9E FE FE D1 E3 D1   .."..."t........
0B49:0050  E3 8B 87 BE 22 8B 97 C0-22 89 86 FA FE 89 96 FC   ...."...".......
0B49:0060  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B49:0070  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P

它们是连续存放的~:)
经过分析发现原来源代码中将这些数据定义在一个标号所段内,它们就是连续存放的。如果分开定义,它们就是单独占FH。


下面对比单独定义数据,看它们是如何分布的~:)

  Quote:
              assume    cs:code,ds:data_1
      data_1  segment
              db        'Redtek'
      data_1  ends
      data_2  segment
              db        '2006.12.27'
      data_2  ends
        code  segment
      start:  mov       ax,4c00h
              int       21h
        code  ends
              end       start

Debug 分析它的分布……

  Quote:
-d ss:0
0B49:0000  52 65 64 74 65 6B 00 00-00 00 00 00 00 00 00 00   Redtek..........
0B49:0010  32 30 30 36 2E 31 32 2E-32 37 00 00 00 00 00 00   2006.12.27......

上面这两段数据的“间隔”起始偏移地址都相差FH,为了取首地址和定位方便~:)


将字符串中的第一行 “BaSiC” 字母全转成大写
              assume    cs:codesg,ds:datasg
      datasg  segment
              db        'BaSiC'
              db        'iNfOrmaTiOn'
      datasg  ends
      codesg  segment
      start:  mov       ax,datasg
              mov       ds,ax       ; 取数据段首地址
              mov       bx,0
              mov       cx,5
          s:  mov       al,ds:[bx]
              and       al,11011111b            ; 转所有字母为大写
              mov       ds:[bx],al
              inc       bx
              loop      s
              mov       ax,4c00h
              int       21h
      codesg  ends
              end       start
跟踪结果

  Quote:
-d ds:0
0B49:0000  42 41 53 49 43 69 4E 66-4F 72 6D 61 54 69 4F 6E   BASICiNfOrmaTiOn

把第一行字符串全转成大写,第二行字符串全转成小写字母
              assume    cs:codesg,ds:datasg
      datasg  segment
              db        'BaSiC'
              db        'iNfOrmaTiOn'
      datasg  ends
      codesg  segment
      start:  mov       ax,datasg
              mov       ds,ax       ; 取数据段首地址
              mov       bx,0
              mov       cx,5
          s:  mov       al,ds:[bx]
              and       al,11011111b            ; 转所有字母为大写
              mov       ds:[bx],al
              inc       bx
              loop      s

              mov       bx,5
              mov       cx,11       ; 设置循环11次,因为字母长度为11
         s1:  mov       al,[bx]
              or        al,00100000b
              mov       [bx],al
              inc       bx
              loop      s1

              mov       ax,4c00h
              int       21h
      codesg  ends
              end       start
用 Debug 跟踪结果

  Quote:
-d ds:0
0B49:0000  42 41 53 49 43 69 6E 66-6F 72 6D 61 74 69 6F 6E   BASICinformation

如果要是用到批处理里呢?
C:\WINDOWS>set /a 0X61 "&" 0xDF
65


C:\WINDOWS>set /a 97 "&" 0XDF
65
C:\WINDOWS>
0X61是小写字母 a 的 ASCII 码的16进制表现形式。a 的10进制 ASCII 码是 97

“&” 符号在批处理的 SET /? 命令帮助中是:“与” 操作。

我们最后结果所得到的 65 ,就是大写字母 A 的 10 进制的 ASCII 码表现形式。

怎么验证 65 就是大写字母 A ? ALT+65 的按键操作就出来了~:)



[bx+idta]


一种更灵活的寻址方式,它的偏移地址为(bx)+idata决定~:)

将一个内存单元的内容送入AX,这个内存单元长度为2字节(字单元),存放一个字,偏移地址为bx中数值+200,段地址在ds中。

数学化描述为: (ax)=((ds)*16+(bx)+200)

该指令也可以写成如下格式:

  mov ax,[200+bx]
  mov ax,200[bx]
  mov ax,[bx].200


下面是随便一个例子,对比上面三种命令格式机器码中的表示。
              assume    cs:code
        code  segment
      start:  mov       ax,'aa'
              mov       bx,10h
              mov       ax,[200+bx]
              mov       ax,200[bx]
              mov       ax,[bx].200

              mov       ax,4c00h
              int       21h

        code  ends
              end       start
Debug 反汇编看到机器码与反汇编指令如下:

  Quote:
0B49:0000 B86161        MOV     AX,6161
0B49:0003 BB1000        MOV     BX,0010
0B49:0006 8B87C800      MOV     AX,[BX+00C8]
0B49:000A 8B87C800      MOV     AX,[BX+00C8]
0B49:000E 8B87C800      MOV     AX,[BX+00C8]
0B49:0012 B8004C        MOV     AX,4C00
0B49:0015 CD21          INT     21

经过对比发现,机器码是一样的,看来这些格式全是MASM干的~:)


用[bx+idata]方式进行数组处理
使用数组原理实现将“两行”不同地址的字符串(第一行转大写,第二行转小写)
              assume    cs:codesg,ds:datasg
      datasg  segment
              db        'BaSiC'
              db        'MinIX'
      datasg  ends
      codesg  segment
      start:  mov       ax,datasg
              mov       ds,ax
              mov       bx,0
              mov       cx,5
          s:  mov       al,[bx]
              and       al,11011111b            ; 转大写
              mov       [bx],al     ; 转换完以后送回原处
              mov       al,5[bx]
              or        al,00100000b            ; 转小写
              mov       5[bx],al    ; 转完小写后再送回去
              inc       bx
              loop      s
              mov       ax,4c00h
              int       21h
      codesg  ends
              end       start
Debug 验证转换结果

  Quote:
-d ds:0
0B49:0000  42 41 53 49 43 6D 69 6E-69 78 00 00 00 00 00 00   BASICminix......

SI 和 DI

SI 和 DI 是 8086CPU中和bx功能相近的寄存器,SI和DI不能够分成两个8位寄存器来使用。

示例三组指令实现了相同的功能:


1) mov bx,0
   mov ax,[bx]

2) mov si,0
   mov ax,[si]

3) mov di,0
   mov ax,[di]

下面三组指令也实现了相同的功能:


1) mov bx,0
   mov ax,[bx+123]

2) mov si,0
   mov ax,[si+123]

3) mov di,0
   mov ax,[di+123]



SI 和 DI 复制字符串到另一地址
              assume    cs:code,ds:data
        data  segment
              db        'welcome to masm!'
              db        '................'
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       si,0        ; 源变址
              mov       di,16       ; 目地变址
              mov       cx,8
          s:  mov       ax,[si]
              mov       [di],ax
              add       si,2
              add       di,2
              loop      s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
更少代码的示例,Debug 分析过程……
              assume    cs:code,ds:data
        data  segment
              db        'welcome to masm!'
              db        '................'
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       si,0
              mov       cx,8
          s:  mov       ax,0[si]
              mov       16[si],ax
              add       si,2
              loop      s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
[bx+si] 和 [bx+di]


例: mov ax,[bx+si] 的含义
   将一个内存单元的内容(字单元)送入ax寄存器,偏移地址为bx+si,段地址在ds中。

描述: (ax)=((ds)*16+(bx)+(si))

Debug 查看……
              assume    cs:code

        code  segment
      start:  mov       ax,2000h
              mov       ds,ax
              mov       bx,1000h
              mov       si,0
              mov       ax,[bx+si]
              inc       si
              mov       cx,[bx+si]
              inc       si
              mov       di,si
              add       cx,[bx+di]

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
[bx+si+idata] 和 [bx+di+idata]

数学描述: (ax)=((ds)*16+(si)+(idata))
              assume    cs:code
        code  segment
      start:  mov       ax,2000h
              mov       ds,ax
              mov       bx,1000h
              mov       si,0
              mov       ax,[bx+2+si]
              inc       si
              mov       cx,[bx+2+si]
              inc       si
              mov       di,si
              mov       bx,[bx+2+di]
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
代码对比
0B49:0000 B80020        MOV     AX,2000
0B49:0003 8ED8          MOV     DS,AX
0B49:0005 BB0010        MOV     BX,1000
0B49:0008 BE0000        MOV     SI,0000
0B49:000B 8B4002        MOV     AX,[BX+SI+02]
0B49:000E 46            INC     SI
0B49:000F 8B4802        MOV     CX,[BX+SI+02]
0B49:0012 46            INC     SI
0B49:0013 8BFE          MOV     DI,SI
0B49:0015 8B5902        MOV     BX,[BX+DI+02]
0B49:0018 B8004C        MOV     AX,4C00
0B49:001B CD21          INT     21
使用上面原理操作多维数组


将每个单词的第一个字母改为大写字母~:)
              assume    cs:code,ds:data
        data  segment
              db        '1. file         '
              db        '2. edit         '
              db        '3. search       '
              db        '4. view         '
              db        '5. options      '
              db        '6. help         '
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       si,3        ; 定位在第3列
              mov       bx,0        ; 定位在第n行
              mov       cx,6
          s:  mov       al,[bx+si]  ; 取数组 bx行.si列
              and       al,11011111b            ; 第5位为0就是大写
              mov       [bx+si],al  ; 送回去~:)
              add       bx,10h
              loop      s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
Debug 验证结果
-d ds:0
0B49:0000  31 2E 20 46 69 6C 65 20-20 20 20 20 20 20 20 20   1. File
0B49:0010  32 2E 20 45 64 69 74 20-20 20 20 20 20 20 20 20   2. Edit
0B49:0020  33 2E 20 53 65 61 72 63-68 20 20 20 20 20 20 20   3. Search
0B49:0030  34 2E 20 56 69 65 77 20-20 20 20 20 20 20 20 20   4. View
0B49:0040  35 2E 20 4F 70 74 69 6F-6E 73 20 20 20 20 20 20   5. Options
0B49:0050  36 2E 20 48 65 6C 70 20-20 20 20 20 20 20 20 20   6. Help
多重循环转换数组中所有字母为大写
              assume    cs:code,ds:data
        data  segment
              db        'dos             '
              db        'del             '
              db        'set             '
              db        'for             '
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       bx,0
              mov       cx,4
         s0:  mov       di,0        ; 数组列指针
              mov       dx,cx       ; 保存外层循环cx计数器的值
              mov       cx,3        ; 转换列计数(共3列)
         s1:  mov       al,[bx+di]  ; 取数组第0列
              and       al,11011111b
              mov       [bx+di],al  ; 转完大写送回去
              inc       di          ; 数组列指针右移一列
              loop      s1
              mov       cx,dx       ; 恢复外层循环cx计数器的值
              add       bx,10h      ; 将数组指针下移一行
              loop      s0          ; 返回外层循环
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
Debug 调试结果~:)))
-d ds:0
0B49:0000  44 4F 53 20 20 20 20 20-20 20 20 20 20 20 20 20   DOS
0B49:0010  44 45 4C 20 20 20 20 20-20 20 20 20 20 20 20 20   DEL
0B49:0020  53 45 54 20 20 20 20 20-20 20 20 20 20 20 20 20   SET
0B49:0030  46 4F 52 20 20 20 20 20-20 20 20 20 20 20 20 20   FOR
不管有多少个内、外循环,都只有一个cx计数器。
上面代码是用寄存器暂存数据。

用内存暂存数据
              assume    cs:code,ds:data
        data  segment
              db        'dos             '
              db        'del             '
              db        'set             '
              db        'for             '
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       bx,0
              mov       cx,4        ; 外循环计数器
         s0:  mov       ds:[40h],cx
              mov       si,0
              mov       cx,3        ; 内循环计数器
          s:  mov       al,[bx+si]  ; 内循环
              and       al,11011111b
              mov       [bx+si],al  ; 转完大写再送回去
              inc       si
              loop      s
              add       bx,10h      ; 外循环数组指针下移一行
              mov       cx,ds:[40h] ; 从内存单元中取出数据恢复cx计数器
              loop      s0
              mov       ax,4c00h
              int       21h
        code  ends
              end       start

-d ds:0
0B49:0000  44 4F 53 20 20 20 20 20-20 20 20 20 20 20 20 20   DOS
0B49:0010  44 45 4C 20 20 20 20 20-20 20 20 20 20 20 20 20   DEL
0B49:0020  53 45 54 20 20 20 20 20-20 20 20 20 20 20 20 20   SET
0B49:0030  46 4F 52 20 20 20 20 20-20 20 20 20 20 20 20 20   FOR
将菜单中前4个字母大写,使用栈来存储数据
              assume    cs:code,ds:data,ss:stack

       stack  segment
              dw        0,0,0,0,0,0,0,0
       stack  ends

        data  segment
              db        '1. display      '
              db        '2. brows        '
              db        '3. replace      '
              db        '4. modify       '
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       ax,ss
              mov       ss,ax
              mov       sp,0fh
              mov       bx,0
              mov       cx,4
         s0:  push      cx
              mov       si,0
              mov       cx,4
         s1:  mov       al,[bx+3+si]
              and       al,11011111b
              mov       [bx+3+si],al
              inc       si
              loop      s1
              add       bx,10h
              pop       cx
              loop      s0
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
Debug 分析结果
-d ss:0
0B49:0000  00 00 00 00 00 00 00 00-00 2A 00 4E 0B 59 05 00   .........*.N.Y..
0B49:0010  31 2E 20 44 49 53 50 6C-61 79 20 20 20 20 20 20   1. DISPlay
0B49:0020  32 2E 20 42 52 4F 57 73-20 20 20 20 20 20 20 20   2. BROWs
0B49:0030  33 2E 20 52 45 50 4C 61-63 65 20 20 20 20 20 20   3. REPLace
0B49:0040  34 2E 20 4D 4F 44 49 66-79 20 20 20 20 20 20 20   4. MODIfy
[ Last edited by redtek on 2006-12-27 at 10:44 PM ]
作者: redtek     时间: 2006-12-28 12:12    标题: 【80x86汇编语言学习笔记】

数据处理

寄存器(reg)集合包括:  ax,bx,cx,dx,ah,al,bh,bl,ch,cl,dh,dl,sp,bp,si,di;

段寄存器(sreg)集合包括:ds,ss,cs,es;


要处理的数据有多长?

1) 通过寄存器名指明要处理的数据尺寸。

2) 在没有寄存器名存在的情况下,用操作符 X ptr 指明内存单元的长度。
   X 在汇编指令中可以为 word 或 bye。

   mov word ptr ds:[0],1
   inc word ptr [bx]


在没有寄存器参与的内存单元访问指令中,用 word ptr 或 byte ptr 显性地指明所要访问的内存单地长度是必要的。
否则CPU无法得知所要访问的单元是字单元还是字节单元。

  Quote:
-a
0AF5:0100 mov ax,2000
0AF5:0103 mov ds,ax
0AF5:0105 mov byte ptr [1000],1
0AF5:010A
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80020        MOV     AX,2000
-t

AX=2000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 8ED8          MOV     DS,AX
-t

AX=2000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=2000  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 C606001001    MOV     BYTE PTR [1000],01                 DS:1000=00
-t

AX=2000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=2000  ES=0AF5  SS=0AF5  CS=0AF5  IP=010A   NV UP EI PL NZ NA PO NC
0AF5:010A CD21          INT     21
-d ds:1000
2000:1000  01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
2000:1010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
2000:1020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
2000:1030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
2000:1040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
2000:1050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
2000:1060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
2000:1070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

寻址方式的缩合应用

更改内存中的数据
公司名称: DEC
总载姓名: Ken Olsen
排  名: 137
收  入: 40(40亿美元)
著名产品: PDP(小型机)

              db        'DEC'       ; 公司名称
              db        'Ken Oslen' ; 总载姓名
              dw        137         ; 排  名
              dw        40          ; 收  入
              db        'PDP'       ; 著名产品
数据在内存中存放形式
0B49:0000  44 45 43 4B 65 6E 20 4F-73 6C 65 6E 89 00 28 00   DECKen Oslen..(.
0B49:0010  50 44 50 00 00 00 00 00-00 00 00 00 00 00 00 00   PDP.............

代码如下

              assume    cs:code,ds:data
        data  segment
              db        'DEC'       ; 公司名称
              db        'Ken Oslen' ; 总载姓名
              dw        137         ; 排  名
              dw        40          ; 收  入
              db        'PDP'       ; 著名产品
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       bx,0        ; 没用,就是表示寻址原理,超始位置
              mov       word ptr [bx+0ch],38    ; 修改排名为38
              add       word ptr [bx+0eh],70    ; 收入字段增加70
              mov       si,0        ; 用si定位产品字符串中的字符
              mov       byte ptr [bx+10h+si],'V'
              inc       si          ; 下移一个
              mov       byte ptr [bx+10h+si],'A'
              inc       si
              mov       byte ptr [bx+10h+si],'X'
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
修改完成
0B49:0000  44 45 43 4B 65 6E 20 4F-73 6C 65 6E 26 00 6E 00   DECKen Oslen&.n.
0B49:0010  56 41 58 00 00 00 00 00-00 00 00 00 00 00 00 00   VAX.............
很象结构体

按C风格描述
/* 定义一个公司记录的结构体 */

struct company {
        char cn[3] ;                 /* 公司名称 */
        char hn[9] ;                 /* 总载姓名 */
        int pm ;                     /* 排    名 */
        int sr ;                     /* 收    入 */
        char cp[3] ;                 /* 著名产品 */
}
按C风格,用汇编语言重写上面操作数据的代码
              assume    cs:code,ds:data
        data  segment
              db        'DEC'       ; 公司名称
              db        'Ken Oslen' ; 总载姓名
              dw        137         ; 排  名
              dw        40          ; 收  入
              db        'PDP'       ; 著名产品
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       bx,0
              mov       word ptr [bx].0ch,38    ; 排名 - C: dec.pm=38
              add       word ptr [bx].0eh,70    ; 收入 - C: dec.sr=dec.sr+70
              mov       si,0
              mov       byte ptr [bx].10h[si],'V'           ; C: dec.cp[i]='V'
              inc       si          ; C: i++
              mov       byte ptr [bx].10h[si],'A'           ; C: dec.cp[i]='A'
              inc       si          ; C: i++
              mov       byte ptr [bx].10h[si],'X'           ; C: dec.cp[i]='X'
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
在机器码中,上面不同书写的代码在机器码中只有一种表示
0B4B:0000 B8490B        MOV     AX,0B49
0B4B:0003 8ED8          MOV     DS,AX
0B4B:0005 BB0000        MOV     BX,0000
0B4B:0008 C7470C2600    MOV     WORD PTR [BX+0C],0026
0B4B:000D 83470E46      ADD     WORD PTR [BX+0E],+46
0B4B:0011 BE0000        MOV     SI,0000
0B4B:0014 C6401056      MOV     BYTE PTR [BX+SI+10],56
0B4B:0018 46            INC     SI
0B4B:0019 C6401041      MOV     BYTE PTR [BX+SI+10],41
0B4B:001D 46            INC     SI
0B4B:001E C6401058      MOV     BYTE PTR [BX+SI+10],58
DIV 除法操作

1) 除数:有8位和16位两种
2) 被除数:默认放在AX或DX和AX中。
   如果除数为8位,被除数则为16位。默认在AX中存放。
   如果除数为16位,被除数则为32位,在DX和AX中存放。DX存高16位,AX存低16位。
3) 结果
   如果除数为8位,则AL存放商,AH存放余数。
   如果除数为16位数,则AX存放商,DX存放余数

格式:

   div reg
   div 内存单元

   例: div byte ptr ds:[0]
  含义: (al)=(ax)/((ds)*16+0))商
      (ah)=(ax)/(ds)*16+0))余数

   div word ptr es:[0]
   (ax)=[(dx)*10000H+(ax)]/((es)*16+0)  商
   (dx)=[(dx)*10000H+(ax)]/((es)*16+0)  余数

   div byte ptr [bx+si+8]
   (al)=(ax)/((ds)*16+(bx)+(si)+8) 商
   (ah)=(ax)/(ds)*16+(bx)+(si)+8) 余数

   div word ptr [bx+si+8]
   (ax)=[(dx)*10000H+(ax)]/((ds)*16+(bx)+(si)+8) 商
   (dx)=[(dx)*10000H+(ax)]/((ds)*16+(bx)+(si)+8) 余数


计算 100001 / 100

100001(16)=186A1H
100(16)=64H

如果用 Debug 直接写汇编代码,一切都是16进制。
如果用 Masm 汇编代码写再编译,随便。

被除数是32位,那么计算除得的商与余数就必须(商16位、余数占16位),除数也占16位。

186A1H(被除数)的位低是:86A1H 、高位是:0001H


Debug 直接汇编代码如下:
C:\Masm50>debug
-A
0AF5:0100 mov dx,1
0AF5:0103 mov ax,86a1
0AF5:0106 mov bx,64
0AF5:0109 div bx
0AF5:010B
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 BA0100        MOV     DX,0001
-t

AX=0000  BX=0000  CX=0000  DX=0001  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 B8A186        MOV     AX,86A1
-t

AX=86A1  BX=0000  CX=0000  DX=0001  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI PL NZ NA PO NC
0AF5:0106 BB6400        MOV     BX,0064
-t

AX=86A1  BX=0064  CX=0000  DX=0001  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0109   NV UP EI PL NZ NA PO NC
0AF5:0109 F7F3          DIV     BX
-t

AX=03E8  BX=0064  CX=0000  DX=0001  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010B   NV UP EI PL NZ NA PO NC
用 windows 计算器算 100001 / 100 = 1000.01 (十进制)
上面值做为验证之用~:)

商:1000
余数:01

它们的16进制表示:  商:03E8H  余数:0001H
商与余数各占16位,商在低位表示的寄存器中,余数在高位表示的寄存器中。

再看 Debug 结果数据:

  Quote:
AX=03E8  BX=0064  CX=0000  DX=0001  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010B   NV UP EI PL NZ NA PO NC

红色标注表示:商
绿色标注表示:余数 ~:)



Masm 汇编代码,求 1001 / 100
              assume    cs:code
        code  segment
      start:  mov       ax,1001
              mov       bl,100
              div       bl
        code  ends
              end       start
验证计算结果
AX=010A  BX=0064  CX=0007  DX=0000  SP=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0007
AX=010A,结果正确~:)
商:0AH , 余数: 01H



伪指令 dd


定义dword(double word)-双字型数据。
双字型数据占 32 位。

如果汇编代码: dd 1
则16进制表示其所占用的空间: 00000001H


用 div 计算 data 段中第一个数据除以第二个数据后的结果,商存放在第3个数据的存储单元中。
              assume    cs:code,ds:data
        data  segment
              dd        100001      ; 32位被除数
              dw        100         ; 16位除数
              dw        0
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       ax,ds:[0]   ; 将被除数低位送入低位被除数应该在的寄存器
              mov       dx,ds:[2]   ; 将被除数的高位送dx寄存器
              div       word ptr ds:[4]         ; 用dx:ax 中的32位数据除以 ds:4 字单元中的数据
              mov       ds:[6],ax   ; 将商存储在 ds:6 字单元中
              mov       ax,4c00h
              int       21h
        code  ends
              end       start


  Quote:
-d ds:0006
0B49:0000                    E8 03-00 00 00 00 00 00 00 00

DUP 操作符

它与 db,dw,dd等数据定义伪指令配合使用,用来进行数据的重复。

db  重重的次数 dup (重复的字节型数据)
dw  重重的次数 dup (重复的字型数据)
dd  重重的次数 dup (重复的双字型数据)
              assume    cs:code,ss:stack

       stack  segment
              db        200 dup(0)  ; 定义容量为200字节
       stack  ends

        code  segment
      start:  mov       ax,4c00h
              int       21h
        code  ends
              end       start
数据分布

  Quote:
-d ss:0
0B49:0000  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:0010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:0020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:0030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:0040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:0050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:0060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:0070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
-d
0B49:0080  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:0090  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:00A0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:00B0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0B49:00C0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

寻址方式在结构化数据访问中的应用

[ Last edited by redtek on 2006-12-28 at 06:19 PM ]
作者: redtek     时间: 2006-12-29 22:32    标题: 【80x86汇编语言学习笔记】

转移指令

可以修改IP或同时修改CS和IP的指令统称为转移指令。
转移指令就是可以控制CPU执行内存中某处的代码的指令……


短转移IP的修改范围: -128~127
近转移IP的修改范围: -32768~32767

8086的CPU转移指令分为以下几类:

  无条件转移指令(JMP)
  条件转移指令
  循环指令(LOOP)
  过程
  中断


操作符 offset
              assume    cs:code

        code  segment
      start:  mov       ax,offset start         ; 相当于 mov ax,0
          s:  mov       ax,offset s ; 取s标号处的指令偏移地址……

        code  ends
              end       start
Debiug ……

  Quote:
0B49:0000 B80000        MOV     AX,0000
0B49:0003 B80300        MOV     AX,0003

offset操作符取得了标号start和s的偏移地址。


复制start指令到s0处
              assume    cs:code
        code  segment
      start:  mov       ax,bx
              mov       si,offset start
              mov       di,offset s0
              mov       ax,cs:[si]
              mov       cs:[di],ax
         s0:  nop
              nop
        code  ends
              end       start
复制的过程……

  Quote:
-r
AX=0000  BX=0000  CX=0010  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 8BC3          MOV     AX,BX
-t

AX=0000  BX=0000  CX=0010  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0002   NV UP EI PL NZ NA PO NC
0B49:0002 BE0000        MOV     SI,0000
-t

AX=0000  BX=0000  CX=0010  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0005   NV UP EI PL NZ NA PO NC
0B49:0005 BF0E00        MOV     DI,000E
-t

AX=0000  BX=0000  CX=0010  DX=0000  SP=0000  BP=0000  SI=0000  DI=000E
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0008   NV UP EI PL NZ NA PO NC
0B49:0008 2E            CS:
0B49:0009 8B04          MOV     AX,[SI]                            CS:0000=C38B
-t

AX=C38B  BX=0000  CX=0010  DX=0000  SP=0000  BP=0000  SI=0000  DI=000E
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000B   NV UP EI PL NZ NA PO NC
0B49:000B 2E            CS:
0B49:000C 8905          MOV     [DI],AX                            CS:000E=9090
-t

AX=C38B  BX=0000  CX=0010  DX=0000  SP=0000  BP=0000  SI=0000  DI=000E
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000E   NV UP EI PL NZ NA PO NC
0B49:000E 8BC3          MOV     AX,BX
-t

AX=0000  BX=0000  CX=0010  DX=0000  SP=0000  BP=0000  SI=0000  DI=000E
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0010   NV UP EI PL NZ NA PO NC

JMP指令

段间转移、段内短转移、段内近转移

范围是指字节。

jmp short s  段内短转移
jmp near ptr  段内近转移
jmp far ptr s  段间转移

段间转移
              assume    cs:code

        code  segment
      start:  mov       ax,0
              mov       bx,0
              jmp       far ptr s
              db        256 dup(0)
          s:  add       ax,1
              inc       ax
        code  ends
              end       start


  Quote:
0B49:0000 B80000        MOV     AX,0000
0B49:0003 BB0000        MOV     BX,0000
0B49:0006 EA0B01490B    JMP     0B49:010B

机器码: EA0B01490B 是目的地址在指令中的存储顺序。
(存放次序->低地址:高地址)即逆序存放。

低地址16位,高地址16位。
高地址代表转移的段地址,低地址代表偏移量。
0B49:010B 目标转移地址在机器码中段地址与偏移地址是逆序存放,段与偏移地址中的高位与低位也是逆序存放~:)

看来编译器在将指令(不管是高级语言还是汇编),最终全是机器码。
只不过这个机器码,不同的机器码代表不同的指令,机器码后面跟着数据,
这个数据是前或后(带符号数)或是所赋的值,然后CPU执行到当前指令时将当前IP+当前指令的长度,就算出下一条指令的地址,然后将 ip指过去。
这个时候CPU再执行已取回的指令,然后执行。
当遇到要转移的指令时,算出要转移到哪里,然后再次修改IP的值指向转移的地址(无论它是8位还是16位还是32位-段间转移),
然后执行IP所指向的指令。
如果当前执行的指令不是转移指令,则执行完后就执行上一次取这次指令时已修改的IP的值,所以它直接执行就行了。

如果用批处理做一个极简单的编译器,那么一个是机器码(这是固定),机器码后面跟着不同的数据,需要编译器来计算它们前后的位移量。

理论上可以实现(至于批处理文件--文本文件无法直接存二进制数据),
但也不是没有办法~:)
且VBS可以向文件写入二进制数据,那么完全可以用VBS做编译器。
乱想……

简单的说,段内转移,段间转移,近转移这些名词,实际上只不过不同的机器码后面跟着8位(段内近转移),它是带符号数,也就是表示这么多寻址能力,当然是近转移。在CPU看来,只不过是 机器码后面跟着一个8位地址。

而近转移,只不过就是一个机器码后面跟着一个16位的地址数据。
16位所能表示出来的带符号数也就那么多。机器还是一样。

而被MASM编译器喜欢的语法,什么JMP FAR PTR 的段间转移,看这指令太长,到了机器码那里还不是: 机器码,后面跟着32位的地址数据。
而这个地址数据还是按照INTEL喜欢的逆序存放,低位后面是高位。
而那个32位的转移地址也是一样,先低后高,不管它什么段地址:偏移量,
到了最后还是段*16+偏移量,所以那个高位(后面的)自然就是表示段,用低位表示偏移量。反正到了CPU那里最后还是物理地址,这些段和偏移量只不过是读着方便和编程方便,它是给我们方便而看的。

按照书上的解释再举一反三的拔开那些迷雾……


寻找机器码,先看看 mov 指令的机器码与寄存器它们的表示:
mov ax,0000
mov bx,0000
mov cx,0000
mov dx,0000
Debug分析如下:

  Quote:
C:\Masm50>debug
-A
0AF5:0100 MOV AX,0
0AF5:0103 MOV BX,0
0AF5:0106 MOV CX,0
0AF5:0109 MOV DX,0
0AF5:010C

-U 100 109
0AF5:0100 B80000        MOV     AX,0000
0AF5:0103 BB0000        MOV     BX,0000
0AF5:0106 B90000        MOV     CX,0000
0AF5:0109 BA0000        MOV     DX,0000

得到 MOV AX 的机器码是 B8
   MOV BX   …… 是 BB
   MOV CX   …… 是 B9
   MOV DX   …… 是 BA (这些将来被CPU执行的时候都是二进制0011..所表示的“高”、“低”的电讯号…(先瞎想)

假如要开发向BX寄存器赋 0123H (23存在低位,01存在高位),它们存储状态的表示是 2301H,先读高位再读低位是为了视觉上的方便。而Inter要表示的是低位内存地址存低位,高位内存地址存高位。

现在写 MOV CX 这一个命令的编译器。向CX寄存器赋值 0123H(10进制291)。……(略……)
调用Debug……
理论上相当于

MOV AX,0123
Echo  B82301>Demo.com
理论上是这样,但Echo不能直接写入二进制到文件。什么语言能写,什么语言就能够实现。

或是
chcp 437
echo ╕#☺>a.com
用Debug装入还是 mov ax,0123


转移地址在内存中的jmp指令的两种格式

从内存单元地址处开始存放着一个字,是转移的目的偏移地址。


1) jmp word ptr 内存单元地址(段内转移)

2) jmp dword ptr 内存单元地址(段间转移)
              assume    cs:code
        code  segment
      start:  mov       ax,0123h
              mov       ds:[0],ax
              mov       word ptr ds:[2],0
              jmp       dword ptr ds:[0]        ; 段间转移 Debug: jmp far [0000]

              mov       ax,4c00h
              int       21h


        code  ends
              end       start
Debug 对比指令

  Quote:
C:\Masm50>debug p170.exe
-r
AX=0000  BX=0000  CX=0015  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 B82301        MOV     AX,0123
-u
0B49:0000 B82301        MOV     AX,0123
0B49:0003 A30000        MOV     [0000],AX
0B49:0006 C70602000000  MOV     WORD PTR [0002],0000
0B49:000C FF2E0000      JMP     FAR [0000]
0B49:0010 B8004C        MOV     AX,4C00
0B49:0013 CD21          INT     21
0B49:0015 05508D        ADD     AX,8D50
0B49:0018 46            INC     SI
0B49:0019 8050E83E      ADC     BYTE PTR [BX+SI-18],3E
0B49:001D 0D83C4        OR      AX,C483
-t

AX=0123  BX=0000  CX=0015  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0003   NV UP EI PL NZ NA PO NC
0B49:0003 A30000        MOV     [0000],AX                          DS:0000=20CD
-t

AX=0123  BX=0000  CX=0015  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0006   NV UP EI PL NZ NA PO NC
0B49:0006 C70602000000  MOV     WORD PTR [0002],0000               DS:0002=9FFF
-t

AX=0123  BX=0000  CX=0015  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000C   NV UP EI PL NZ NA PO NC
0B49:000C FF2E0000      JMP     FAR [0000]                         DS:0000=0123
-t

AX=0123  BX=0000  CX=0015  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0000  IP=0123   NV UP EI PL NZ NA PO NC
0000:0123 F0            LOCK
0000:0124 F3            REPZ
0000:0125 EE            OUT     DX,AL
-q

使程序中的jmp指令执行后,CS:IP指向程序的第一条指令,在data段中应该定义哪些数据?

  Quote:
              assume    cs:code

        data  segment
              db        0ffh    ; 界标置
              dw        0
              db        0ffh    ; 边界标置~:)

        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       bx,0
              jmp       word ptr [bx+1]

        code  ends
              end       start

使jmp指令执行后,CS:IP指向程序的第一条指令
              assume    cs:code

        data  segment
              dd        12345678h
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       bx,0

              mov       [bx],bx
              mov       [bx+2],cs
              jmp       dword ptr ds:[0]

        code  ends
              end       start
假如内存 2000:1000 BE 00 06 00 00 00 ...
跟据下面指令(不运行代码)直接写出结果 (CS)=?  IP=?

mov       ax,2000h
mov       es,ax
jmp       dword ptr es:[1000h]
低位:高位,所以 CS=0006,IP=00BE


JCZX指令

cx=0时退出,8位位移,相当于 if((cx)==0) jmp short 标号。

利用jcxz指令,实现在内存2000H段中查找第一个值为0的字节,找到后将它的偏移地址存储在dx中。
              assume    cs:code

        code  segment
      start:  mov       ax,2000h
              mov       ds,ax
              mov       bx,0
          s:  mov       cl,[bx]     ; 传送比对是否为0的数据
              jcxz      ok
              inc       bx
              jmp       short s
         ok:  mov       dx,bx
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
运行前提交“安排”数据
-d ds:0
2000:0000  FF FF FF 00 FF 00 00 00-00 00 00 00 00 00 00 00
2000:0010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
Loop指令

(cx)=(cx)-1    if (cx) 不等于 0 ... (ip)=(ip)+8位有符号数位移。
相当于C表示的: (cx) -- ;

Debug分析代码执行过程……
              assume    cs:code
        code  segment
              mov       ax,4c00h
              int       21h

      start:  mov       ax,0
          s:  nop
              nop

              mov       di,offset s
              mov       si,offset s2
              mov       ax,cs:[si]  ; ax=s2地址内容
              mov       cs:[di],ax  ; cs代码段=s2地址内的指令

         s0:  jmp       short s
         s1:  mov       ax,0
              int       21h
              mov       ax,0

         s2:  jmp       short s1
              nop

        code  ends
              end       start
写视频缓冲区(直接写屏)彩色字符串
              assume    cs:code
        data  segment
              db        'welcome to masm!'
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       ax,0b800h
              mov       es,ax
              mov       ah,00000010b
              mov       di,0        ; 列
              mov       cx,16
          s:  mov       al,ds:[si]
              mov       es:[159*10+32*2+di],ax
              inc       ah
              inc       si          ; 读进下一个
              add       di,2        ; 显示下一列字符
              loop      s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
运行视图

内存地址: B8000H~~BFFFFH 共32KB的空间,为80*25彩色字符模式的显示缓冲区。它分为8页。

低位:字符ASCII,高位:色值(RGB)二进制表示0~7个位的“开关”。

[ Last edited by redtek on 2006-12-30 at 01:32 PM ]
附件 1: 1.GIF (2006-12-31 02:18, 6.08 K, 下载附件所需积分 1点 ,下载次数: 2)



作者: redtek     时间: 2006-12-31 03:57    标题: 【80x86汇编语言学习笔记】

call和ret指令

ret指令用栈中的数据,修改ip的内容,从而实现近转移;
retf指令用栈中的数据,修改cs和ip的内容,从而实现远转移。

CPU执行ret指令时,进行下面两步操作:

  (ip)=((ss)*16+(sp))
  (sp)=(sp)+2


CPU执行retf指令时:进行下面四步操作:

  (ip)=((ss)*16+(sp))
  (sp)=(sp)+2
  (cs)=((ss)*16+(sp))
  (sp)=(sp)+2


CPU执行ret指令时,相当于进行:
  pop ip

CPU执行retf指令时,相当于进行:
  pop ip
  pop cs

ret
              assume    cs:code

       stack  segment
              db        16 dup (0)
       stack  ends

        code  segment
              mov       ax,4c00h
              int       21h
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,16
              mov       ax,0
              push      ax
              mov       bx,0
              ret
        code  ends
              end       start

retf使用

              assume    cs:code

       stack  segment
              db        16 dup (0)
       stack  ends

        code  segment
              mov       ax,4c00h
              int       21h
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,16

              mov       ax,0
              push      cs
              push      ax
              mov       bx,0
              retf
        code  ends
              end       start


  Quote:
AX=0000  BX=0000  CX=0026  DX=0000  SP=000C  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0012   NV UP EI PL NZ NA PO NC
0B4A:0012 BB0000        MOV     BX,0000
-t

AX=0000  BX=0000  CX=0026  DX=0000  SP=000C  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0015   NV UP EI PL NZ NA PO NC
0B4A:0015 CB            RETF
-t

AX=0000  BX=0000  CX=0026  DX=0000  SP=0010  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0000   NV UP EI PL NZ NA PO NC
0B4A:0000 B8004C        MOV     AX,4C00

根据PUSH压栈次序对调发现:

当执行retf时,CPU处理的时候是先计算低位,再计算高位。
所以,压栈是先进后出,所以要先压高位再压低位,才可以保证retf执行时先恢复高位后恢复低位,否则出错(相返)。


实现从内存 1000:0000 处开始执行指令
              assume    cs:code
       stack  segment
              db        16 dup (0)
       stack  ends

        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,16
              mov       ax,1000h
              push      ax          ; 先压高位再压低位,将来retf时cpu会默认先计算低位。
              mov       ax,0
              push      ax
              retf
        code  ends
              end       start
Debug跟踪结果

  Quote:
AX=0000  BX=0000  CX=0021  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0000   NV UP EI PL NZ NA PO NC
0B4A:0000 B8490B        MOV     AX,0B49
-t

AX=0B49  BX=0000  CX=0021  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0003   NV UP EI PL NZ NA PO NC
0B4A:0003 8ED0          MOV     SS,AX
-t

AX=0B49  BX=0000  CX=0021  DX=0000  SP=0010  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0008   NV UP EI PL NZ NA PO NC
0B4A:0008 B80010        MOV     AX,1000
-t

AX=1000  BX=0000  CX=0021  DX=0000  SP=0010  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=000B   NV UP EI PL NZ NA PO NC
0B4A:000B 50            PUSH    AX
-t

AX=1000  BX=0000  CX=0021  DX=0000  SP=000E  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=000C   NV UP EI PL NZ NA PO NC
0B4A:000C B80000        MOV     AX,0000
-t

AX=0000  BX=0000  CX=0021  DX=0000  SP=000E  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=000F   NV UP EI PL NZ NA PO NC
0B4A:000F 50            PUSH    AX
-t

AX=0000  BX=0000  CX=0021  DX=0000  SP=000C  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0010   NV UP EI PL NZ NA PO NC
0B4A:0010 CB            RETF
-t

AX=0000  BX=0000  CX=0021  DX=0000  SP=0010  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=1000  IP=0000   NV UP EI PL NZ NA PO NC
1000:0000 83D200        ADC     DX,+00

call指令

CPU执行call指令时,进行两步操作:

  将当前的IP或CS和IP压入栈中;
  转移。


call不能实现短转移。call指令实现转移的方法和jmp指令的原理相同。


call 标号(将当前的IP压栈后,转到标号处执行指令)

CPU执行此种格式的call指令时,进行如下操作:

  1) (sp)=(sp-2)
     (ss)*16+(sp)=(ip)
  2) (IP)=(IP)+16位位移

16位位移由编译程序在编译时算出

相当于 CPU 执行指令 "call 标号" 时进行:
  
  push  IP
  jmp  near ptr 标号


下面程序执行后,ax中的数值为多少?
              assume    cs:code
        code  segment
      start:  mov       ax,0
              call      s
              inc       ax
          s:  pop       ax
        code  ends
              end       start
答:执行过程如下:
  
  1) CPU读入 mov ax,0 ,然后将 ip 指向下一条指令
  2) 执行 mov ax,0 以后,读入当前ip所指向的指令(call s)
  3) 读入 call s,然后将ip指向下一条指令 inc ax(它的IP是6)
  3) 执行 call s 指令-》将当前IP值压栈(6),然后执行跳转……S标号……
  4) CPU读入指令 pop ax ,将ip指向下一条指令。
  5) ……最后 AX=0006 

表面上看上去不应该是6,而应该是当前 call s 所在的指令ip地址,但实际上按照CPU执行以及设置IP的次序,它依上述所示。


转移的目的地址在指令中的call指令


指令 "call far ptr 标号" 实现的是段间转移。

CPU执行此种格式的call指令时,进行如下操作:

  1) (sp)=(sp)-2       先压高位
     ((ss)*16+(sp))=(cs)
     (sp)=(sp)-2
     ((ss)*16+(sp))=(IP)   再压低位
  2) (CS)=标号所在段的段地址
     (IP)=标号所在段的偏多地址

相当于:

  push CS
  push IP
  jmp far ptr 标号


分析
0B49:0000 B80000        MOV     AX,0000
0B49:0003 9A0900490B    CALL    0B49:0009
0B49:0008 40            INC     AX
0B49:0009 58            POP     AX
0B49:000A 03C0          ADD     AX,AX
0B49:000C 5B            POP     BX
0B49:000D 03C3          ADD     AX,BX
-t

AX=0000  BX=0000  CX=000F  DX=0000  SP=FFFC  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0009   NV UP EI PL NZ NA PO NC
0B49:0009 58            POP     AX
-d ss:fffc
0B49:FFF0                                      08 00 49 0B
-t

AX=0008  BX=0000  CX=000F  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000A   NV UP EI PL NZ NA PO NC
0B49:000A 03C0          ADD     AX,AX
-t

AX=0010  BX=0000  CX=000F  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000C   NV UP EI PL NZ AC PO NC
0B49:000C 5B            POP     BX
-t

AX=0010  BX=0B49  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000D   NV UP EI PL NZ AC PO NC
0B49:000D 03C3          ADD     AX,BX
-t

AX=0B59  BX=0B49  CX=000F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000F   NV UP EI PL NZ NA PE NC
转移地址在寄存器中的call指令


指令格式: call 16位寄存器


功能:

  (sp)=(sp-2)
  ((ss)*16+(sp))=(IP)
  (IP)=(16位寄存器)

CPU执行 call 16位reg时,相当于进行:

  push IP
  jmp 16位寄存器

实验
              assume    cs:code
       stack  segment
              db        16 dup (0)
       stack  ends

        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,16
              mov       ax,0000
              call      ax
              inc       ax
              mov       bp,sp
              add       ax,[bp]
        code  ends
              end       start
1) 压当前IP到栈(只压IP),压了ip是为了Call完以后回来时执行ip指向的地址指令
2) jmp 取指令16位寄存器中要求跳转的地址
-t

AX=0B49  BX=0000  CX=0023  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0003   NV UP EI PL NZ NA PO NC
0B4A:0003 8ED0          MOV     SS,AX
-t

AX=0B49  BX=0000  CX=0023  DX=0000  SP=0010  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0008   NV UP EI PL NZ NA PO NC
0B4A:0008 B80000        MOV     AX,0000
-t

AX=0000  BX=0000  CX=0023  DX=0000  SP=0010  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=000B   NV UP EI PL NZ NA PO NC
0B4A:000B FFD0          CALL    AX
-t

AX=0000  BX=0000  CX=0023  DX=0000  SP=000E  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0000   NV UP EI PL NZ NA PO NC
0B4A:0000 B8490B        MOV     AX,0B49
-d ss:e
0B49:0000                                            0D 00                 ..
转移地址在内存中的call指令


两种格式:

1) call word ptr 内存单元地址
2) call dword ptr 内存单元地址]

这两种格式都是先压入IP,然后取要跳转的指定内存单元地址。

call word ptr ...
              assume    cs:code
        data  segment
              db        16 dup (0)
        data  ends

       stack  segment
              db        16 dup (0)
       stack  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       ax,stack
              mov       ss,ax
              mov       sp,10h
              mov       ax,0123h
              mov       ds:[0],ax
              call      word ptr ds:[0]
        code  ends
              end       start
call dword ptr 内存单元地址

dword是32位,所以所取的内存单元地址中足够“包含”(段:偏移量)地址。
但CPU处理次序是先低位再高位,所以必须先压(段)再压(偏移:就是IP),当CPU处理时才可以先处理低地址再高地址

所以,dword内存单元中(双字)排列次序:低位:高位
在sp中存放的是ip地址(16位)(字)

当CPU执行 call dword ptr 内存单元地址时,相当于进行:

  push CS
  push IP
  jmp dword ptr 内存单元地址

即:栈内的数据存的是Call完以后回来时要运行的指令地址(也要保持将来先取到低位再取到高位)
  而内存单元内存的数据(低位:高低)是指要 jmp dword ptr 的地址~:)
              assume    cs:code
        data  segment
              db        16 dup (0)
        data  ends

       stack  segment
              db        16 dup (0)
       stack  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       ax,stack
              mov       ss,ax
              mov       sp,10h
              mov       ax,0123h
              mov       ds:[0],ax
              mov       word ptr ds:[2],0
              call      dword ptr ds:[0]
        code  ends
              end       start
最后 bx中的值是8


call和ret配合使用

              assume    cs:code
        code  segment
      start:  mov       ax,1
              mov       cx,3
              call      s
              mov       bx,ax
              mov       ax,4c00h
              int       21h
          s:  add       ax,ax
              loop      s
              ret
        code  ends
              end       start
当执行完“子程序段”时,由 ret 指令将call调用前压入栈中的IP再弹出来,这样就恢复的调用前的IP,程序继续执行~:)

关于call与ret
              assume    cs:code
       stack  segment
              db        8 dup (0)
              db        8 dup(0)
       stack  ends

        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,16
              mov       ax,1000
              call      s
              mov       ax,4c00h
              int       21h

          s:  add       ax,ax
              ret
        code  ends
              end       start
mul乘法指令

两个相乘的数,要么都是8位,要么都是16位。

计算:

  如果是8位,一个默认放在AH中,另一个放在8位寄存器或是内存字节单元中。
  如果是16位,一个默认在AX中,另一个放在16位寄存器或内存字单元中。

结果:


  如果是8位乘法,结果默认放在AL中。
  如果是16位乘法,结果高位默认放在DX中,低位在AX中存放。

格式: mul reg 或 mul 内存单元


内存单地可以用不同的寻址方式给出:

  mul byte ptr ds:[0]
  含义:(ax)=(dl)*((ds)*16+0)

mul word ptr [bx+si+8]
含义:(ax)=((ax)*(ds)*16+(bx)+(si)+8) 结果的低16位
   (dx)=((ax)*(ds)*16+(bx)+(si)+8) 结果的高16位

计算 100*10000
C:\Masm50>debug
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B464          MOV     AH,64
-a
0AF5:0100 mov ax,64
0AF5:0103 mov bx,2710
0AF5:0106 mul bx
0AF5:0108
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B86400        MOV     AX,0064
-t

AX=0064  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 BB1027        MOV     BX,2710
-t

AX=0064  BX=2710  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI PL NZ NA PO NC
0AF5:0106 F7E3          MUL     BX
-t

AX=4240  BX=2710  CX=0000  DX=000F  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0108   OV UP EI PL NZ NA PO CY
0AF5:0108 46            INC     SI
-q
模块化程序设计
参数和结果传递的问题

计算data段中第一组数据的3次方,结果保存在后面一组dword单元中
              assume    cs:code
        data  segment
              dw        1,2,3,4,5,6,7,8
              dd        0,0,0,0,0,0,0,0
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       si,0        ; 指向第一组word单元
              mov       di,16       ; 指向第二组dword单元

              mov       cx,8
          s:  mov       bx,[si]
              call      cube
              mov       [di],ax     ; 先写低位
              mov       [di].2,dx   ; 再写高位
              add       si,2
              add       di,4
              loop      s
              mov       ax,4c00h
              int       21h

       cube:  mov       ax,bx       ; 3次方的计算结果存在:ax(低位),dx(高位)
              mul       bx
              mul       bx
              ret
        code  ends
              end       start
结果

  Quote:
-d ds:0
0B49:0000  01 00 02 00 03 00 04 00-05 00 06 00 07 00 08 00
0B49:0010  01 00 00 00 08 00 00 00-1B 00 00 00 40 00 00 00
0B49:0020  7D 00 00 00 D8 00 00 00-57 01 00 00 00 02 00 00

批量数据传送

将批量数据放到内存中,然后将它们所在的内存空间的首地址放在寄存器中,传递给需要的子程序。

即,相当于是按址传送数据。刚才上面的寄存器传送最终结果,相当于按值似递。在c、vb中均有类似的说明~:0


将data段中的字符串转为大写
              assume    cs:code

        data  segment
              db        'conversation'
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       si,0
              mov       cx,12
              call      capital
              mov       ax,4c00h
              int       21

    capital:  and       byte ptr [si],11011111b ; 将ds:si所指单元中的字母转大写
              inc       si
              loop      capital
              ret

        code  ends
              end       start
Debug 过程

  Quote:
C:\Masm50>debug p190.exe
-r
AX=0000  BX=0000  CX=002A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0000   NV UP EI PL NZ NA PO NC
0B4A:0000 B8490B        MOV     AX,0B49
-u
0B4A:0000 B8490B        MOV     AX,0B49
0B4A:0003 8ED8          MOV     DS,AX
0B4A:0005 BE0000        MOV     SI,0000
0B4A:0008 B90C00        MOV     CX,000C
0B4A:000B E80500        CALL    0013
0B4A:000E B8004C        MOV     AX,4C00
0B4A:0011 CD15          INT     15
0B4A:0013 8024DF        AND     BYTE PTR [SI],DF
0B4A:0016 46            INC     SI
0B4A:0017 E2FA          LOOP    0013
0B4A:0019 C3            RET
0B4A:001A E1E2          LOOPZ   FFFE
0B4A:001C F786E1E8DDE2  TEST    WORD PTR [BP+E8E1],E2DD
-g 000e

AX=0B49  BX=0000  CX=0000  DX=0000  SP=0000  BP=0000  SI=000C  DI=0000
DS=0B49  ES=0B39  SS=0B49  CS=0B4A  IP=000E   NV UP EI PL NZ NA PE NC
0B4A:000E B8004C        MOV     AX,4C00
-d ss:0
0B49:0000  43 4F 4E 56 45 52 53 41-54 49 4F 4E 00 00 00 00   CONVERSATION....

编写子程序注意的问题:

  子程序开始: 子程序中使用的寄存器入栈
         子程序内容
         子程序中使用的寄存器出栈
         返回(ret,retf)

子程序例程:用子程序转所有字母为大写
              assume    cs:code
        data  segment
              db        'word',0
              db        'unix',0
              db        'wind',0
              db        'good',0
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       bx,0

              mov       cx,4
          s:  mov       si,bx
              call      capital
              add       bx,5
              loop      s
              mov       ax,4c00h
              int       21h

    capital:  push      cx
              push      si
     change:  mov       cl,[si]
              mov       ch,0
              jcxz      ok
              and       byte ptr [si],11011111b
              inc       si
              jmp       short change
         ok:  pop       si
              pop       cx
              ret

        code  ends
              end       start
上面子程序内,使用栈先保存当前寄存器状态,然后再执行子程序内部计算过程,计算完后再弹出保存的值,不与调用者冲突。

  Quote:
-d ss:0
0B49:0000  57 4F 52 44 00 55 4E 49-58 00 57 49 4E 44 00 47   WORD.UNIX.WIND.G
0B49:0010  4F 4F 44 00 00 00 00 00-00 00 00 00 00 00 00 00   OOD.............

编写子程序

要求:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
   dh=行号(取值0~24),dl=列号(取值范围0~79)
   cl=颜色, ds:si指向字符串的首地址

   在屏幕的8行3列,用绿色显示 data 段中的字符串。
              assume    cs:code
        data  segment
              db        'Welcome to masm!',0
        data  ends

        code  segment
      start:  mov       dh,8        ; 子程序入口参数: 行=dh; 列=dl; 颜色=cl
              mov       dl,3
              mov       cl,2
              mov       ax,data
              mov       ds,ax
              mov       si,0
              call      show_str    ; Call 子程序

              mov       ax,4c00h
              int       21h

   show_str:  push      dx          ; 保护寄存器值
              push      cx
              push      si

              mov       ax,0b800h
              mov       es,ax

              mov       ax,160      ; dx=行
              mul       dh
              mov       dx,ax
              xor       ax,ax
              mov       al,2
              mul       cl          ; ax=列
              add       ax,dx
              add       bx,ax       ; 最终行、列在 bx 寄存器中

        str:  mov       ch,0
              mov       cl,[si]
              jcxz      ok
              mov       ch,2
              mov       es:[bx+di],cx
              inc       si
              add       di,2
              jmp       short str

         ok:  pop       si          ; 恢复寄存器值
              pop       cx
              pop       dx
              ret

        code  ends
              end       start
上面显示方式是直接写显存(直接写屏)方式,在CMD下运行编译的.exe无效,Debug内运行有效。MS-DOS 环境下直接运行编译的.exe文件有效。

[ Last edited by redtek on 2007-1-1 at 02:02 PM ]
附件 1: 1.GIF (2007-1-2 03:00, 11.55 K, 下载附件所需积分 1点 ,下载次数: 2)



作者: redtek     时间: 2007-1-2 04:04    标题: 【80x86汇编语言学习笔记】

解决除法溢出问题

例如下面代码将会溢出:(1000 / 1)
C:\Masm50>debug
-a
0AF5:0100 mov bh,1
0AF5:0102 mov ax,03e8
0AF5:0105 div bh
0AF5:0107
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B701          MOV     BH,01
-t

AX=0000  BX=0100  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0102   NV UP EI PL NZ NA PO NC
0AF5:0102 B8E803        MOV     AX,03E8
-t

AX=03E8  BX=0100  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 F6F7          DIV     BH
-t

Divide overflow
子程序描述:

  名称: divdw
  功能: 进行不会产生溢出的除法运算。被除数为dword型,除数为word型,结果为dword型。

下面公式将可能产生溢出的除法运算,转变为多个不会产生溢出的除法运算。

公式: X/N=int(H/N)*6636 + [rem(H/N)*65536+L]/N]

    商:(被除数高16位 / 除数) * 65536 + [ 余数:(被除数高16位 / 除数) * 65536 + 被除数低16位 ] / 除数


返回:(dx)=结果的高16位
   (ax)=结果的低16位
   (cx)=余数


根据公式写出子程序以及完整代码:
;---------------------------------------------------
;             计算 1000000/10 (F4240H/0AH)
;                  2000005/10 (1E8485/0AH) =  200000.5 (  30D40H  .5H )
;                  2500005/10 (2625A5/0AH) =  250000.5 (  3D090H  .5H )
;---------------------------------------------------

              assume    cs:code
        code  segment
      start:  mov       ax,25a5h    ; 被除数低16位
              mov       dx,0026h    ; 被除数高16位
              mov       cx,0ah      ; 除数
              call      divdw       ; Call 计算无溢出除法子程序

              mov       ax,4c00h
              int       21h


      divdw:  mov       bx,ax       ; 保存被除数低16位的值
              mov       ax,dx
              mov       dx,0
              div       cx          ; 计算被除数高16位 / 除数)
                                    ; ax=商 dx=余数
              mov       di,dx       ; 保存余数到 di 寄存器
              mov       dx,1
              mul       dx
              mov       si,ax       ; 计算公式:  商:(被除数高16位 / 除数) * 65536

                                    ; ---------------------------------------------
                                    ; BX=被除数低16位   DI=余数   SI=公式左边结果(高16位)
                                    ; ---------------------------------------------

              mov       dx,di       ; 省略了 低16位余数*65536的步骤,直接升低16位为高位代替计算
              mov       ax,bx       ; 送入被除数低16位
              xor       bx,bx       ; 清零
              div       cx          ; 计算公式右边: [ 余数 + 被除数低16位 ] / 除数

                                    ; ---------------------------------------------
                                    ; AX=商   DX=余数
                                    ; ---------------------------------------------

              mov       cx,dx       ; 返回值: CX=余数
              mov       dx,si       ; 放入公式左边高位

                                    ; --------------------------------- 返回值 ----
                                    ; AX=低16位  DX=高16位  CX=余数
                                    ; ---------------------------------------------

              xor       si,si
              xor       di,di       ; 返回前清零
              ret

        code  ends
              end       start
结果
;             ============ 计算 2500005/10 (2625A5/0AH) =  250000.5 (  3D090H  .5H ) ====
;
;             AX=D090  BX=0000  CX=0005  DX=0003  SP=0000  BP=0000  SI=0000  DI=0000
;             DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000C   NV UP EI PL ZR NA PE NC
;             0B49:000C B8004C        MOV     AX,4C00
;
;             ===========================================================================
[ Last edited by redtek on 2007-1-3 at 01:46 PM ]
作者: 220110     时间: 2007-1-2 12:09
推荐你看<<黑客反调试技术>>.

PS:这不是一本黑客技术的书,着重是反调试技术.从正常软件生产来看掌握调试技术是必要的,但从不道德的角度,反调试就是破解,反编译.这书有相当的难度.
作者: redtek     时间: 2007-1-4 02:37
非常感谢220110版主推荐~:)

在当当网找到一本《黑客反汇编揭秘》,不知道是不是这本书?

还有一本叫做《黑客调试技术揭秘》,作者:(美)卡斯帕克尔。
不知道《黑客反调试技术揭秘》和《黑客调试技术揭秘》这两本是不是指同一本书?

《黑客调试技术揭秘》的目录:
第1部分 调试工具入门
 第1章 调试工具简介
 第2章 在UNIX环境中进行调试的特性
 第3章 模拟调试器和仿真器
 第4章 用BoundsChecker进行应用程序分析
第2部分 调试工具入门
 第5章 保护机制简介
 第6章 熟悉调试器
 第7章 IDA崭露头角
 第8章 注册保护机制之道
 第9章 散列及其克服
 第10章 常见的用于演示版的保护机制
第3部分 反调试技术
 第11章 反调试技术简介
 第12章 各种各样的反调试技术
 第13章 UNIX特有的反调试技术
 第14章 可自我修改的代码
 第15章 使用隐含的自我控制来创建不可破解的保护
 第16章 智力调试
 第17章 软件保护
 第18章 如何使你的应用程序更可靠
 第19章 软件测试
第4部分 应用程序和操作系统的严重错误
 第20章 应用程序和操作系统的严重错误简介
 第21章 战兢苟活还是出死入生
 第22章 如何利用内转储
第5部分 PE文件
 第23章 PE文件格式
 第24章 PE文件中插入和删除代码的技术
附盘说明
[ Last edited by redtek on 2007-1-3 at 01:43 PM ]
作者: redtek     时间: 2007-1-4 03:22    标题: 【80x86汇编语言学习笔记】

子程序

数值显示

将数据用十进制形式显示到屏幕上,进行两步工作:

  1) 将二进制信息存储的数据转变为十进制形式的字符串;
  2) 显示十进制表式的字符串

以数字表示的16进制(如 1 , 2, 3, ... )与数字的ASCII码之间相差30H,即1H:如果希望显示1,则必须1H+30H才等于它的ASCII码。

编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。
              assume    cs:code
        data  segment
              db        10 dup (0)
        data  ends

        code  segment
      start:  mov       ax,12666
              mov       bx,data
              mov       ds,bx
              mov       si,0
              call      dtoc        ; 调用二进制转十进制子程序

              mov       dh,8
              mov       dl,3
              mov       cl,2
              call      show_str    ; 调用显示字符串子程序

              mov       ax,4c00h
              int       21h

       dtoc:  mov       bx,10       ; 除以10取余数分解成单个数字
         s1:  mov       cx,ax       ; 为jcxz提供判断是否除尽的依据
              jcxz      dtoc_ret    ; 除尽时,当cx为0时结束子程序
              xor       dx,dx       ; 清零
              div       bx          ; AX=商   DX=余数
              add       dx,30h      ; 转十进制数码值为十进制数码字符对应的ASCII码
              mov       [si],dl     ; 存储单个数字
              inc       si          ; 指针下移
              jmp       s1          ; 如果没除尽,则继续除

   dtoc_ret:  dec       si
              xor       bx,bx
              xor       si,si
              ret


   show_str:  xor       di,di
              mov       bx,0b800h   ; 视频缓冲区起始
              mov       es,bx
              mov       ax,160      ; 每行字符数
              mul       dh          ; 计算n行所占用的字符串,结果在AX中
              mov       bx,ax       ; 显示字符串超始偏移量(行)
              xor       ax,ax
              mov       al,2
              mul       dl          ; 计算n列所占用字符数
              add       bx,ax       ; BX=最终显示字符串偏移量
              mov       dl,cl       ; 将颜色送入不与下面计算冲突的寄存器中
       view:  mov       ah,dl       ; 定义字符显示颜色
              mov       al,[si]
              mov       ch,0
              mov       cl,al
              jcxz      ok
              mov       es:[bx+di],ax           ; 将要显示的字符送入视频缓冲区
              inc       si
              add       di,2
              jmp       short view
         ok:  xor       ax,ax
              xor       bx,bx
              xor       cx,cx
              xor       di,di
              xor       si,si
              ret


        code  ends
              end       start
因为使用了要显示的数字除以10,做为分解的每一个位余数+30H就等于要显示的这个数字的ASCII,所以出来的是倒序了。

(所以相代码未完,需要显示前倒序一次就变成可以显示的正确的数字了)

这回可以正序显示了~:)
              assume    cs:code
        data  segment
              db        10 dup (0)
        data  ends

        code  segment
      start:  mov       ax,12666
              mov       bx,data
              mov       ds,bx
              mov       si,0
              call      dtoc        ; 调用二进制转十进制子程序

              mov       dh,8
              mov       dl,3
              mov       cl,2
              call      show_str    ; 调用显示字符串子程序

              mov       ax,4c00h
              int       21h

       dtoc:  mov       bx,20h
              mov       ss,bx
              mov       sp,20
              mov       bx,10       ; 除以10取余数分解成单个数字
         s1:  mov       cx,ax       ; 为jcxz提供判断是否除尽的依据
              jcxz      dtoc_ret    ; 除尽时,当cx为0时结束子程序
              xor       dx,dx       ; 清零
              div       bx          ; AX=商   DX=余数
              add       dx,30h      ; 转十进制数码值为十进制数码字符对应的ASCII码
              push      dx          ; 将余数压栈,方便将倒序的数字正序存放
              inc       si          ; 指针下移
              jmp       s1          ; 如果没除尽,则继续除

   dtoc_ret:  mov       cx,si       ; 取到几个余数就循环几次
              xor       si,si
         s2:  pop       ax          ; al (低位)存有要正序的数字ASCII码,将其送入ds
              mov       [si],al
              inc       si
              loop      s2
              xor       bx,bx
              xor       si,si
              xor       ax,ax
              ret


   show_str:  xor       di,di
              mov       bx,0b800h   ; 视频缓冲区起始
              mov       es,bx
              mov       ax,160      ; 每行字符数
              mul       dh          ; 计算n行所占用的字符串,结果在AX中
              mov       bx,ax       ; 显示字符串超始偏移量(行)
              xor       ax,ax
              mov       al,2
              mul       dl          ; 计算n列所占用字符数
              add       bx,ax       ; BX=最终显示字符串偏移量
              mov       dl,cl       ; 将颜色送入不与下面计算冲突的寄存器中
       view:  mov       ah,dl       ; 定义字符显示颜色
              mov       al,[si]
              mov       ch,0
              mov       cl,al
              jcxz      ok
              mov       es:[bx+di],ax           ; 将要显示的字符送入视频缓冲区
              inc       si
              add       di,2
              jmp       short view
         ok:  xor       ax,ax
              xor       bx,bx
              xor       cx,cx
              xor       di,di
              xor       si,si
              ret

        code  ends
              end       start
[ Last edited by redtek on 2007-1-4 at 04:53 PM ]
作者: amao     时间: 2007-1-4 04:08
对我来说有如看天书啊。
作者: redtek     时间: 2007-1-5 03:09
哈哈……
我每往后学一点儿都像看天书一样~:)
不过难就难吧,一天一点儿,总有掌握它的这一天~:)
作者: redtek     时间: 2007-1-5 07:10    标题: 【80x86汇编语言学习笔记】

实验 寻址方式在结构化数据访问中的应用
年份         收入(千美元)        雇员(人)        人均收入(千美元)
======================================================================

1975          16                      3                    ?
1976          22                      7                    ?
1977          382                     9                    ?
1978          1356                    13                   ?
1979          2390                    28                   ?
1980          8000                    38                   ?
1981          16000                   130                  ?
1982          24486                   220                  ?
1983          50065                   476                  ?
1984          97479                   778                  ?
1985          140417                  1001                 ?
1986          197514                  1442                 ?
1987          345980                  2258                 ?
1988          590827                  2793                 ?
1989          803530                  4037                 ?
1990          1183000                 5635                 ?
1991          1843000                 8226                 ?
1992          2759000                 11542                ?
1993          3753000                 14430                ?
1994          4649000                 15257                ?
1995          5937000                 17800                ?

======================================================================
代码如下:
              assume    cs:code,ds:data,es:table
        data  segment
              db        '1975','1976','1977','1978','1979','1980','1981','1982','1983'
              db        '1984','1985','1986','1987','1988','1989','1990','1991','1992'
              db        '1993','1994','1995'
              dd        16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
              dd        345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
              dw        3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
              dw        11542,14430,15257,17800
        data  ends
       table  segment
              db        21 dup ('year summ ne ?? ')
       table  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax       ; DS=要读取的数据段
              mov       ax,table
              mov       es,ax       ; ES=要写入的数据段
              mov       bx,0        ; 行偏移量
              mov       si,0        ; 读取偏移量
              mov       di,0        ; 写入偏移量
              mov       dx,0
              mov       cx,21
              mov       ax,20h      ; 分配栈空间
              mov       ss,ax
              mov       sp,20
          s:  mov       ax,ds:[si].0            ; 读取年份后再写入es段
              mov       es:[bx].0,ax
              mov       ax,ds:[si].2
              mov       es:[bx].2,ax
              mov       ax,ds:[4*21+si].0       ; 读收入, dword 型数据
              mov       es:[bx].5,ax            ; 写
              mov       ax,ds:[4*21+si].2
              mov       es:[bx].7,ax            ; 写收入数据
              push      si          ; 保存当前si的值
              mov       si,bp       ; word 型公司雇员数据每次只递增2
              mov       ax,ds:[4*21+4*21+si].0  ; 公司雇员
              mov       es:[bx].10,ax
              add       bp,2
              pop       si          ; 恢复si偏移量的值
              push      cx
              mov       ax,ds:[4*21+si].0
              mov       dx,ds:[4*21+si].2
              mov       cx,es:[bx].10
              div       cx
              mov       es:[bx].13,ax           ; 写入人均收入
              pop       cx          ; 恢复cx寄存器
              add       si,4
              add       bx,10h
              loop      s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
运行结果
-d ds:0
0B49:0000  31 39 37 35 31 39 37 36-31 39 37 37 31 39 37 38   197519761977197
0B49:0010  31 39 37 39 31 39 38 30-31 39 38 31 31 39 38 32   197919801981198
0B49:0020  31 39 38 33 31 39 38 34-31 39 38 35 31 39 38 36   198319841985198
0B49:0030  31 39 38 37 31 39 38 38-31 39 38 39 31 39 39 30   198719881989199
0B49:0040  31 39 39 31 31 39 39 32-31 39 39 33 31 39 39 34   199119921993199
0B49:0050  31 39 39 35 10 00 00 00-16 00 00 00 7E 01 00 00   1995........~..
0B49:0060  4C 05 00 00 56 09 00 00-40 1F 00 00 80 3E 00 00   L...V...@....>.
0B49:0070  A6 5F 00 00 91 C3 00 00-C7 7C 01 00 81 24 02 00   ._.......|...$.
-d es:0
0B57:0000  31 39 37 35 20 10 00 00-00 20 03 00 20 05 00 20   1975 .... .. ..
0B57:0010  31 39 37 36 20 16 00 00-00 20 07 00 20 03 00 20   1976 .... .. ..
0B57:0020  31 39 37 37 20 7E 01 00-00 20 09 00 20 2A 00 20   1977 ~... .. *.
0B57:0030  31 39 37 38 20 4C 05 00-00 20 0D 00 20 68 00 20   1978 L... .. h.
0B57:0040  31 39 37 39 20 56 09 00-00 20 1C 00 20 55 00 20   1979 V... .. U.
0B57:0050  31 39 38 30 20 40 1F 00-00 20 26 00 20 D2 00 20   1980 @... &. ..
0B57:0060  31 39 38 31 20 80 3E 00-00 20 82 00 20 7B 00 20   1981 .>.. .. {.
0B57:0070  31 39 38 32 20 A6 5F 00-00 20 DC 00 20 6F 00 20   1982 ._.. .. o.
-d
0B57:0080  31 39 38 33 20 91 C3 00-00 20 DC 01 20 69 00 20   1983 .... .. i.
0B57:0090  31 39 38 34 20 C7 7C 01-00 20 0A 03 20 7D 00 20   1984 .|.. .. }.
0B57:00A0  31 39 38 35 20 81 24 02-00 20 E9 03 20 8C 00 20   1985 .$.. .. ..
0B57:00B0  31 39 38 36 20 8A 03 03-00 20 A2 05 20 88 00 20   1986 .... .. ..
0B57:00C0  31 39 38 37 20 7C 47 05-00 20 D2 08 20 99 00 20   1987 |G.. .. ..
0B57:00D0  31 39 38 38 20 EB 03 09-00 20 E9 0A 20 D3 00 20   1988 .... .. ..
0B57:00E0  31 39 38 39 20 CA 42 0C-00 20 C5 0F 20 C7 00 20   1989 .B.. .. ..
0B57:00F0  31 39 39 30 20 18 0D 12-00 20 03 16 20 D1 00 20   1990 .... .. ..
-d
0B57:0100  31 39 39 31 20 38 1F 1C-00 20 22 20 20 E0 00 20   1991 8... "  ..
0B57:0110  31 39 39 32 20 58 19 2A-00 20 16 2D 20 EF 00 20   1992 X.*. .- ..
0B57:0120  31 39 39 33 20 28 44 39-00 20 5E 38 20 04 01 20   1993 (D9. ^8 ..
0B57:0130  31 39 39 34 20 28 F0 46-00 20 99 3B 20 30 01 20   1994 (.F. .; 0.
0B57:0140  31 39 39 35 20 68 97 5A-00 20 88 45 20 4D 01 20   1995 h.Z. .E M.
[ Last edited by redtek on 2007-1-5 at 04:28 PM ]
作者: 9527     时间: 2007-1-6 00:27
redtek兄,我也想学学,能把你现在看的书名或者电子书还有网站告诉我吗?期待中ing........
作者: redtek     时间: 2007-1-6 03:52
因为书名相同但出版社不同的书很多,所以列出在当当网可以查到的详细关于这本书的介绍信息~:)


(下面这本书如果是自学,极难。整本都是望而生畏的内容,很可能看几眼就再也不想学汇编了。)
(但其内容非常详细,教学方式适合学校有专业老师辅导。)
(但用某些汇编快速入门的书自学一段时间后,还必须要看这本书,因其内容很全)


Intel 80X86/Pentium汇编语言程序设计(第二版)
http://product.dangdang.com/product.aspx?product_id=8936113




(下面这本书适合在没有任何人帮助你的情况下,完全自学汇编语言)
(很少有望而生畏的内容--因全是入门内容)
(不适合学校教学之用--因为适合完全自学,所以此书教学速度极慢)

汇编语言
http://product.dangdang.com/product.aspx?product_id=8732001



(下面这本书内容非常深,从最简单的十几句汇编语言写一个极微小的“操作系统”到完成一个操作系统的每一步底层)
(看完这本书就比较精通操作系统底层并可以自己写较简单的操作系统了……)
(要求熟悉汇编与C语言--但主要以C语言介绍如何写操作系统)


自己动手写操作系统
http://product.dangdang.com/product.aspx?product_id=9024751




(希望精通磁盘和准备自己从头到尾的写一个Linux操作系统出来,看这本书)

Linux 0.01内核分析与操作系统设计(附CD—ROM光盘一张)——创作你自己的操作系统
http://product.dangdang.com/product.aspx?product_id=8956136




建议不要找电子版的书。电子版的书不适合出门带着看、爬在床上看、坐在椅子上在任何地方看、随时翻着看……

其它资料就是lxmxn在这个网志里介绍的那个网站和Google.com上可以找到的网站~:)

一定要花钱买,强迫自己要不就学出来!要不就白花钱惩罚自己~:)
如果不强迫自己花钱买,很可能遇到难的地方就主动放弃了~:)
作者: 9527     时间: 2007-1-6 04:11
谢谢redtek兄提供学习资料,是啊,有时候不给自己一点压力是不成啦,会变的很懒散很颓废很是没用啊
作者: redtek     时间: 2007-1-6 04:17    标题: 【80x86汇编语言学习笔记】

标置寄存器

ZF标志
flag的第6位,记录相关指令执行后其结果是否为0。0-ZF=1 1-ZF=0

add,mul,div,inc,or,and等运算指令,都会影响标志寄存器。
mov,push,pop等指令对标志寄存器没有影响。

PF标志
flag的第2位是PF,奇偶标志位。记录相关指令执行后,结果的所有二进制位中1的个数是否为偶数,偶数:PF=1,奇数:PF=0

SF标志
flag的第7位是SF,符号标志位。记录相关指令执行后,结果是否为负。负:SF=1,非负:SF=0
它是对有符号数运算结果的一种记录,它记录数据的正负。对于无符号数据计算,SF的值没有意义。


FLAG标置位寄存器结构:
15 14 13 12 11 10  9   8  7   6  5  4   3  2  1  0
===================================================
            OF DF IF   TF SF ZF     AF     PF    CF
在8086 CPU中flag的1、3、5、12、13、14、15没有使用。
在Debug中是 “NV UP EI PL NZ NA PO NC” 次序出现的。
其标置为1还是为0,在Debug中是以其它字母缩写表示的。为1或是为0表示的字母都不一样。

例如:第6位ZF标志(是否为0。结果为零=1,结果非零=0)
在 Debug 中表示其“原始”状态,即均为0时的状态:

  Quote:
NV UP EI PL NZ NA PO NC

NZ就是CF标置的当前表示状态。

至于NZ为什么要出现在从右数向左数的第6位(表面上看是第4位),因为是从FLAG寄存器的低位0表示。
而1、3、5位在8086CPU中未使用,所以“空”过它们,ZF标志自然就出现在了表面上的第4位(实际第6位)。

ZF:零标置位。
   当结果为0时,ZF=1,用ZR表示(出现在Debug中)。
   当结果为1时,ZF=0,用NZ表示。


记忆其特点:

当看到ZR时,表示结果为0,ZF零标置位当结果为0时为真,所以ZF=1。
当看到NZ时,表示结果非0,ZF零标置位当结果非0时为伪,所以ZF=0。


写出下面每条指令执行后,ZF、PF、SF等标志位的值
sub al,al               ZF=1        PF=1        SF=0
mov al,1                ZF=1        PF=1        SF=0
push ax                 ZF=1        PF=1        SF=0
pop bx                  ZF=1        PF=1        SF=0
add al,bl               ZF=0        PF=0        SF=0
add al,10               ZF=0        PF=1        SF=0
mul al                  ZF=0        PF=1        SF=0
CF标志
flag的第0位是CF,进位标志位。
在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高的进位值,或从更高位的借位值。

  Quote:
-a
0AF5:0100 mov al,97
0AF5:0102 sub al,98
0AF5:0104 sub al,al
0AF5:0106
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B097          MOV     AL,97
-t

AX=0097  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0102   NV UP EI PL NZ NA PO NC
0AF5:0102 2C98          SUB     AL,98
-t

AX=00FF  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0104   NV UP EI NG NZ AC PE CY
0AF5:0104 28C0          SUB     AL,AL
-t

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI PL ZR NA PE NC

OF标志
flag的第11位是OF,溢出标志位。
OF记录了有符号数运算的结果是否发生了溢出。溢出:OF=1,没有溢出:OF=0。
对于有符号数而言。


CPU在执行add等指令的时候,包含了两种含义:

  1) 无符号数运算
     CPU用CF位来记录是否产生了进位。

  2) 有符号数运算
     CPU用OF位来记录是否产生了溢出。
     
     用SF位来记录结果的符号。

写出下面每条指令执行后,ZF、PF、SF、CF、OF等标志位的值。

sub al,al       CF=0     OF=0     SF=0    ZF=1    PF=1
mov al,10H      CF=0     OF=0     SF=0    ZF=1    PF=1
add al,90H      CF=0     OF=0     SF=1    ZF=0    PF=1
mov al,80H      CF=0     OF=0     SF=1    ZF=0    PF=1
add al,80H      CF=1     OF=1     SF=1    ZF=1    PF=1
mov al,0FCH     CF=1     OF=1     SF=1    ZF=1    PF=1
add al,05H      CF=1     OF=0     SF=0    ZF=0    PF=0
mov al,7DH      CF=1     OF=0     SF=0    ZF=0    PF=0
add al,0BH      CF=0     OF=1     SF=1    ZF=0    PF=1
DF标志和串传送指令

flag的第10位是DF,方向标志位。
在串处理指令中,控制每次操作后si,di的增减。


DF=0  每次操作后SI,DI递增。
DF=1  每次操作后SI,DI递减。



标置     值为1的标记   值为0的标记
------------------------
DF       DN        UP


格式: movsb
功能: 执行movsb指令相当于进行下面几步操作。

1) ((es)*16+(di))=((ds)*16+(si))
2) 如果DF=0则: (si)=(si)+1
            (di)=(di)+1
   如果DF=1则: (si)=(si)-1
            (di)=(di)-1

movsb的功能是将ds:si指向内存单元中的字节送入es:di中,然后根据标志寄存器DF位的值,将si和di递增或递减。

一般movsb(byte)和movsw(word)都和rep配合使用。



用汇编语言来描述 rep movsb 的功能:
  
  s:  movsb
          loop s



8086 CPU 提供下面两条指令对DF位进行设置:

  cld 指令: 将标志寄存器的DF位置0
  std 指令: 将标志寄存器的DF位置1



例程:将 data 段中的每一个字符串复制到它后面的空间中。
              assume    cs:code
        data  segment
              db        'Welcome to masm!'
              db        16 dup (0)
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       es,ax
              mov       si,0        ; ds:si -> data:0
              mov       di,16       ; es:di -> data:16
              mov       cx,16       ; rep循环16次
              cld                   ; 设 DF=0,正向传送
              rep       movsb       ; 字节传送方式
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
在 Debug 中 cld 与 rep movsb 指令为:
0B4B:0010 FC            CLD
0B4B:0011 F3            REPZ
0B4B:0012 A4            MOVSB
例程:用串传送指令,将F000H段中的最后16个字符复制到data段中。
              assume    cs:code
        data  segment
              db        16 dup (0)
        data  ends

        code  segment
      start:  mov       ax,0f000h
              mov       ds,ax       ; 源 - 读取
              mov       si,0ffffh   ; ds:si -> f000:ffff
              mov       ax,data     ; 目标 - 写入
              mov       es,ax
              mov       di,16-1     ; es:di -> data:16
              mov       cx,16       ; 传送字串长度
              std                   ; 设置 DF=1 逆向传送
              rep       movsb
              mov       ax,4c00h
              int       21h
        code  ends
              end       start


  Quote:
-d es:0
0B49:0000  EA 5B E0 00 F0 30 34 2F-30 32 2F 30 34 00 FC 00   .[...04/02/04...

编程:编写一个子程序,将包含任何字符,以0结尾的字符串中的小写字母变成大写字母。

名称: letterc
功能:同上
参数:ds:si 指向字符串首地址

需要进行转化的是字符串中的小写字母‘a’~‘z’,而不是其他字符。

) 小写字母范围: 97 ~~ (97+26-1)
          97 ~~ 122

) 结束标置:   0
  ;    第一次的代码,故障,略……
调试代码过程中发现一个有意思的错误:)

  Quote:
AX=0B49  BX=0000  CX=0000  DX=0000  SP=FFFE  BP=0000  SI=0033  DI=0000
DS=0B49  ES=0B39  SS=0B49  CS=0B4D  IP=0024   NV UP EI PL NZ NA PE CY
0B4D:0024 B500          MOV     CH,00
-t

AX=0B49  BX=0000  CX=0000  DX=0000  SP=FFFE  BP=0000  SI=0033  DI=0000
DS=0B49  ES=0B39  SS=0B49  CS=0B4D  IP=0026   NV UP EI PL NZ NA PE CY
0B4D:0026 E2EA          LOOP    0012
-d ds:0
0B49:0000  41 42 45 47 49 4E 4E 45-52 27 53 20 41 4C 4C 2D   ABEGINNER'S ALL-
0B49:0010  50 55 52 50 4F 53 45 20-53 59 4D 42 4F 4C 49 43   PURPOSE SYMBOLIC
0B49:0020  20 49 4E 53 54 52 55 43-54 49 4F 4E 20 43 4F 44    INSTRUCTION COD
0B49:0030  45 2E 00 00 00 00 00 00-00 00 00 00 00 00 00 00   E...............
0B49:0040  B8 49 0B 8E D8 BE 00 00-E8 05 00 B8 00 4C CD 21   .I...........L.!
0B49:0050  B5 00 8A 0C 80 F9 61 72-0A 80 F9 7A 77 05 80 E1   ......ar...zw...
0B49:0060  DF 88 0C 46 B5 00 E2 EA-C3 2A E4 40 50 8B C3 05   ...F.....*.@P...
0B49:0070  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P
-r
AX=0B49  BX=0000  CX=0000  DX=0000  SP=FFFE  BP=0000  SI=0033  DI=0000
DS=0B49  ES=0B39  SS=0B49  CS=0B4D  IP=0026   NV UP EI PL NZ NA PE CY
0B4D:0026 E2EA          LOOP    0012
-t

AX=0B49  BX=0000  CX=FFFF  DX=0000  SP=FFFE  BP=0000  SI=0033  DI=0000
DS=0B49  ES=0B39  SS=0B49  CS=0B4D  IP=0012   NV UP EI PL NZ NA PE CY

当 loop 在递减 CX 寄存器的值的时候发生的上面错误。

所以,代码中不使用 loop ... cx了,重新使用 jcxz 命令。
              assume    cs:code
        data  segment
              db        "Beginner's All-purpose Symbolic Instruction Code.",0
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       si,0
              call      letterc     ; Call 转小写字母到大写字母子程序
              mov       ax,4c00h
              int       21h

    letterc:  mov       cx,0
          s:  mov       cl,[si]
              jcxz      return      ; 如果取到字符为 0 ,则结束循环
              cmp       cl,97
              jb        next        ; 如果低于97,则测试下一个字母
              cmp       cl,122
              ja        next        ; 如果高于 122,则测试下一个字母
         ok:  and       cl,11011111b
              mov       [si],cl
       next:  inc       si          ; 递增指针,指向下一个字符
              mov       ch,0
              jmp       short s
     return:  ret
        code  ends
              end       start
遇到一个问题:

在汇编编辑器中,"Beginner”的 'B' 打头字母无论如何更新总是小写方式,后发现在编辑器中 “格式设定” 小写,问题在这里。

将其设置成 “不处理” 即可首字母随意设置。

Debug 调试结果:

  Quote:
-d ss:0
0B49:0000  42 65 67 69 6E 6E 65 72-27 73 20 41 6C 6C 2D 70   Beginner's All-p
0B49:0010  75 72 70 6F 73 65 20 53-79 6D 62 6F 6C 69 63 20   urpose Symbolic
0B49:0020  49 6E 73 74 72 75 63 74-69 6F 6E 20 43 6F 64 65   Instruction Code
0B49:0030  2E 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

[ Last edited by redtek on 2007-1-8 at 02:45 PM ]
作者: redtek     时间: 2007-1-7 07:47    标题: 【80x86汇编语言学习笔记】

ADC指令

功能: 操作对象1 = 操作对象1 + 操作对象2 + CF


adc是带位加法指令,它利用了CF位上记录的进位值。

指令 adc  ax,bx  实现的功能是: (ax)=(ax)+(bx)+CF

  Quote:
C:\Masm50>debug
-a
0AF5:0100 mov ax,2
0AF5:0103 mov bx,1
0AF5:0106 sub bx,ax
0AF5:0108 adc ax,1
0AF5:010B
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80200        MOV     AX,0002
-t

AX=0002  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 BB0100        MOV     BX,0001
-t

AX=0002  BX=0001  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI PL NZ NA PO NC
0AF5:0106 29C3          SUB     BX,AX
-t

AX=0002  BX=FFFF  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0108   NV UP EI NG NZ AC PE CY
0AF5:0108 150100        ADC     AX,0001
-t

(上面:标置寄存器 CF 进位标置:CY代表进位或是借位,在这里是借位)
(当产生借位状态时,CF=1,所以最后结果是 4)


(上面:标置寄存器 SF 符号标置位为: NG(蓝色) 代表负数,其值为 1)


AX=0004  BX=FFFF  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010B   NV UP EI PL NZ NA PO NC

adc指令和add指令相配合可以对更大的数据进行加法运算。



例:计算 1EF000H + 201000H,结果放在AX(高16位)和bx(低16位)中。

  Quote:
C:\Masm50>debug
-a
0AF5:0100 mov ax,001e
0AF5:0103 mov bx,f000
0AF5:0106 add bx,1000
0AF5:010A adc ax,0020
0AF5:010D
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B81E00        MOV     AX,001E
-t

AX=001E  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 BB00F0        MOV     BX,F000
-t

AX=001E  BX=F000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI PL NZ NA PO NC
0AF5:0106 81C30010      ADD     BX,1000
-t

AX=001E  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010A   NV UP EI PL ZR NA PE CY
0AF5:010A 152000        ADC     AX,0020
-t

AX=003F  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010D   NV UP EI PL NZ NA PE NC

先低位与低位相加,结果保存在低位中。
当低位相加产生了进位时,其CF的值为1,Debug中表示:CY(代表进位状态)

然后 adc 指令:高位与高位相加,其相加结果由 adc 指令会自动再与CF相加,因刚刚有了进位,所以高位存放的是进位后的值。
高位与低位的组成就是计算在数相加的总值。


计算 1EF0001000H + 2010001EF0H ,结果放在AX(最高16位),BX(次高16位),CX(低16位)中。

  Quote:
13BF:0100 mov ax,1e
13BF:0103 mov bx,f000
13BF:0106 mov cx,1000
13BF:0109 add cx,1ef0
13BF:010D adc bx,1000
13BF:0111 adc ax,0020
13BF:0114
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0100   NV UP EI PL NZ NA PO NC
13BF:0100 B81E00        MOV     AX,001E
-t

AX=001E  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0103   NV UP EI PL NZ NA PO NC
13BF:0103 BB00F0        MOV     BX,F000
-t

AX=001E  BX=F000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0106   NV UP EI PL NZ NA PO NC
13BF:0106 B90010        MOV     CX,1000
-t

AX=001E  BX=F000  CX=1000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0109   NV UP EI PL NZ NA PO NC
13BF:0109 81C1F01E      ADD     CX,1EF0
-t

AX=001E  BX=F000  CX=2EF0  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=010D   NV UP EI PL NZ NA PE NC
13BF:010D 81D30010      ADC     BX,1000
-t

AX=001E  BX=0000  CX=2EF0  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0111   NV UP EI PL ZR NA PE CY
13BF:0111 152000        ADC     AX,0020
-t

AX=003F  BX=0000  CX=2EF0  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=13BF  ES=13BF  SS=13BF  CS=13BF  IP=0114   NV UP EI PL NZ NA PE NC

两个1024位数字相加
              assume    cs:code
       data1  segment
              dw        1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23
              dw        24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43
              dw        44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63
              dw        64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83
              dw        84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102
              dw        103,104,105,106,107,108,109,110,111,112,113,114,115,116,117
              dw        118,119,120,121,122,123,124,125,126,127,128
       data1  ends

       data2  segment
              dw        1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23
              dw        24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43
              dw        44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63
              dw        64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83
              dw        84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102
              dw        103,104,105,106,107,108,109,110,111,112,113,114,115,116,117
              dw        118,119,120,121,122,123,124,125,126,127,128
       data2  ends

        code  segment
      start:  call      add1024     ; 调用 1024 位相加计算子程序
              mov       ax,4c00h
              int       21h

    add1024:  mov       ax,data1
              mov       ds,ax
              mov       ax,data2
              mov       es,ax
              mov       si,0        ; 初始化
              mov       di,0        ; 内存中低地址放低位,高地址放高位,所以从偏移量0开始
              sub       ax,ax       ; 将CF设置为0
              mov       cx, 64      ; 1024位数字相加需要循环加 64 次
          s:  mov       ax,[si]
              adc       ax,es:[di]
              mov       [si],ax     ; 相加结果放回 data1 段中
              inc       si          ; inc 不会改变CF进位标置的值,递增2准备加下一个16位
              inc       si
              inc       di
              inc       di
              loop      s
              ret
        code  ends
              end       start
sbb指令

sbb是带借位减法指令,它利用了CF位上记录的借位值。

功能:操作对象1 = 操作对象1 -操作对象2 -CF

例: sbb ax,bx  实现的功能是: (ax) = (ax) - (bx) - CF

sbb指令执行后,将对CF进行设置。利用sbb指令可以对任意大的数据进行减法运算。


计算 003E1000H -00202000H,结果放在AX,BX中。

  Quote:
C:\Masm50>debug
-a
0AF5:0100 mov bx,1000
0AF5:0103 mov ax,003e
0AF5:0106 sub bx,2000
0AF5:010A sbb ax,0020
0AF5:010D
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 BB0010        MOV     BX,1000
-t

AX=0000  BX=1000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 B83E00        MOV     AX,003E
-t

AX=003E  BX=1000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI PL NZ NA PO NC
0AF5:0106 81EB0020      SUB     BX,2000
-t

AX=003E  BX=F000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010A   NV UP EI NG NZ NA PE CY
0AF5:010A 1D2000        SBB     AX,0020
-t

AX=001D  BX=F000 CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=010D   NV UP EI PL NZ NA PE NC

当执行 SUB BX,2000 时,就是(1000 -2000 )计算,低位中这个计算必然是负数,而且它需要借位。

所以,紫色 NG 符号标置表示这个计算结果是负数。
   红色 CY 进位/借位标置表示。
  
计算时因为是借位,所以 CF=1,在执行比当前低位再高一点的位的时候,则sbb指令会两个数字相减后再减去CF的值,因为借位了,所以要减去。


pushf 和 popf 指令

pushf的功能是将标志寄存器的值压栈。
popf是从栈中弹出数据,送入标志寄存器中。

pushf和popf,为直接访问标志寄存器提供了一种方法。
              assume    cs:code
        code  segment
      start:  mov       ax,20h
              mov       ss,ax
              mov       sp,16
              mov       ax,0
              push      ax
              popf
              mov       ax,0fff0h
              add       ax,0010h
              pushf
              pop       ax
              and       al,11000101b
              and       ah,00001000b
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
经过分析,pushf压入栈内的标置寄存器的16位值并非与Debug上显示的位置相对应。
因为标置寄存器的第 1、3、5、12、13、14、15位在8086CPU都没有使用(不具有任何含义),所以压栈后的二进制位表示也是这样安排的。

在80586 CPU中标置寄存器的长度可达32位,有如天书一般。

[ Last edited by redtek on 2007-1-8 at 11:22 AM ]
作者: redtek     时间: 2007-1-7 23:01    标题: 【80x86汇编语言学习笔记】

cmp比较指令

cmp的功能相当于减法指令,只是不保存结果。
cmp指令执行后,将对标志寄存器产生影响。
其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。


例: cmp  ax,ax
   相当于: (ax) -(ax) ,结果为0,但并不在ax中保存。
   仅影响 flag 的相关各位。

   指令执行后: ZF=1   PF=1 SF=0    CF=0   OF=O

  Quote:
C:\Masm50>debug
-a
0AF5:0100 mov ax,8
0AF5:0103 mov bx,3
0AF5:0106 cmp ax,bx
0AF5:0108
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80800        MOV     AX,0008
-t

AX=0008  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 BB0300        MOV     BX,0003
-t

AX=0008  BX=0003  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI PL NZ NA PO NC
0AF5:0106 39D8          CMP     AX,BX
-t

AX=0008  BX=0003  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0108   NV UP EI PL NZ NA PE NC

 

PE是奇偶标置位,cmp ax,bx 即(8 - 3)=5。
5的二进制表示:00000101 其中有2个1,也就是说1的出现次数是偶数,所以PF=1。

通过cmp(几乎每次都打成cmd)指令执行后,相关标志位的值可以看出比较结果。


CPU在执行cmp指令的时候,包含两种含义:进行无符号数运算和进行有符号数运算。


关于比较计算与补码的关系:
mov ah,0a0h
mov bh,0cbh
cmp ah,bh
分析如下:

AH=0A0H = -96(十进制)
BH=0CBH = -53(十进制)

CMP AH,BH 相当于减法计算:

  (AH)=(-96)-(-53)
      =(-96)+53
      =-43 (十进制)

得到了-43,现在求有符号数表示(-43的补码):

  |-43| 绝对值为: 43
  43=0010 1011     (二进制表示)
    =1101 0100     (将它求反)
    =1101 0100 + 1 (加1,防止重码)
    =1101 0101     (结果)
    =D5H           (16进制结果:就是-43的补码)

所以,结果是负数,SF符号位标置为1(负数)。


过程


  Quote:
C:\Masm50>debug
-a
0AF5:0100 mov ah,a0
0AF5:0102 mov bh,cb
0AF5:0104 cmp ah,bh
0AF5:0106
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B4A0          MOV     AH,A0
-t

AX=A000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0102   NV UP EI PL NZ NA PO NC
0AF5:0102 B7CB          MOV     BH,CB
-t

AX=A000  BX=CB00  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0104   NV UP EI PL NZ NA PO NC
0AF5:0104 38FC          CMP     AH,BH
-t

AX=A000  BX=CB00  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0106   NV UP EI NG NZ AC PO CY

检测比较结果的条件转移指令

大多数条件转移指令都检测标志寄存器的相关标志位,根据检测的结果来决定是否修改IP。
被cmp指令影响的那些标置位,这些转移指令通常都和cmp相配合使用。


条件转移指令:

je、jne、jb、jnb、ja、jna


上面条件转移指令所检测的标志位,都是cmp指令进行无符号数比较时候,记录比较结果的标志位。

例: je (等于),检测 ZF 位,当ZF=1的时候进行转移。如果在je前面使用了cmp指令,则je对ZF的检测实际上就是间接地检测cmp的比较结果是否为两数相等。

因为 cmp 比较实际上就是两个操作数相减,如果两个操作数值一样,自然相减为0,ZF(是否为0标置位)=1,代表为0。而 je 判断 ZF 标置位,所以就检测到了是相等。

je 检测的是ZF标置,不管 je 前面是什么指令,只要CPU执行JE指令时,ZF=1,那么就发生转移。
cmp ah,bh
je s
如果 ah与bh相等,则 cmp 会将 ZF的值置1,所以后面的 JE 指令判断 ZF=1,则跳转到指定位置。
mov ax,0
add ax,0
je s
虽然没有了 cmp 与 je 的配合,但是 add ax,0 就相当(AX)=(AX)+0 。ax=0,那么ax=ax+0还是0 ,结果为0所以 add 指令将ZF=1。当 je 指令执行时,它判断ZF=1的标置,如果发现ZF=1,则认为相等,进行跳转。

(即,不管是什么指令让CF=1的,只要je判断CF=1,就一定发生跳转)


很多种方法都可以让ZF=1,所以标置寄存器与cmp的原理必须掌握。对这些原理的理解有多少,就意味着对指令深度的理解和灵活开发。


CPU提供了cmp指令,也提供了je等条件转移指令。将它们配合使用,可以实现根据比较结果进行转移的功能。

对于je、jne、jb、jnb、ja、jna等指令和cmp指令配合的使用思和je相同。


例: 在 data 段中统计 8 出现的次数
              assume    cs:code

        data  segment
              db        8,11,8,1,8,5,63,38
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       bx,0
              mov       ax,0
              mov       cx,8
          s:  cmp       byte ptr [bx],8
              jne       next        ; 不等于8 ,则转到 next
              inc       ax          ; 否则(如果相等)就将计数值加1
       next:  inc       bx
              loop      s

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
出现了3次8。

  Quote:
-g 0017

AX=0003  BX=0008  CX=0000  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B49  ES=0B39  SS=0B49  CS=0B4A  IP=0017   NV UP EI PL NZ NA PO NC

例:统计 data 段中数值大于8的字节的个数,用 AX 保存统计结果。
              assume    cs:code

        data  segment
              db        8,11,8,1,8,5,63,38
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       ax,0
              mov       bx,0
              mov       cx,8
          s:  cmp       byte ptr [bx],8
              jna       next        ; 如果不大于8转到next,续继循环
              inc       ax
       next:  inc       bx
              loop      s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
例:统计 data 段中数值小于8的字节个数,用 ax 保存统计结果。
              assume    cs:code

        data  segment
              db        8,11,8,1,8,5,63,38
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       ax,0
              mov       bx,0
              mov       cx,8
          s:  cmp       byte ptr [bx],8
              jnb       next        ; 如果不小于8,则转到next继续循环
              inc       ax          ; 否则(就是大小8)计数器加1
       next:  inc       bx
              loop      s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
[ Last edited by redtek on 2007-1-7 at 08:19 PM ]
作者: redtek     时间: 2007-1-8 00:49    标题: 【80x86汇编语言学习笔记】

Windows计算器使用小窍门:

在汇编语言学习过程中,需要经常用到计算器(Windows CALC.exe)来计算二进制或是进行数字的对比,将数字分组功能打开,就会看到数字间隔,方便一眼略知位数。

[ Last edited by redtek on 2007-1-7 at 11:50 AM ]
附件 1: 1.GIF (2007-1-8 00:49, 18.51 K, 下载附件所需积分 1点 ,下载次数: 2)



作者: redtek     时间: 2007-1-9 03:49    标题: 【80x86汇编语言学习笔记】

内中断

中断信息可以来自CPU的内部和外部。

对于8086CPU,当CPU内部有下面情况发生时,将产生相应的中断信息:

1) 除法错误      (中断类型码:0)
2) 单步执行      (      1)
3) 执行 int0 指令     (      4)
4) 执行 int 指令     (  int n-提供给CPU的中断类型码)


8086CPU 用称为中断类型码的数据来标识中断信息的来源。
中断类型码为一个字节型数据,可以表示256种中断信息的来源。


中断处理程序

根据CPU的设计,中断类型码的作用就是用来定位中断处理程序。
……


中断向量表

CPU用8位的中断类型骊通过中断向量表找到相应的中断处理程序的入口地址。
中断向量表就是中断向量的列表。

中断向量,就是中断处理程序的入口地址。
中断向量表,就是中断处理程序入口地址的列表。

中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口。

CPU只要知道中断类型码,就可将中断类型码作为中断向量表的表项号,定位相应表项,从而得到中断处理程序的入口地址。


内存 0000:0000 到 0000:03E8 的1000个单元中存放着中断向量表。

对于8086CPU,这个入口地址包括段地址和偏移地址,所以一个表项占两个字。高地址字存放段地址,低地址字存放偏移地址。


提问:

1) 4号中断源对应的中断处理程序的入口地址为:(0000:0010)

  Quote:
0000:0000  68 10 A7 00 8B 01 70 00-16 00 A9 03 8B 01 70 00

2) 存储N号中断源对应的中断处理程序入口的偏移地址的内存单元的地址为:(N*4)


3) 存储N号中断源对应的中断处理程序入口的段地址的内存单元的地址为:(N*4+2)



中断过程

8086CPU在收到中断信息后,所引发的中断过程:

  1) 取得中断类型码
  2) 标志寄存器的值入栈
  3) 设置标志寄存器的第8位TF和第9位IF的值为0
  4) CS的内容入栈
  5) IP的内容入栈
  6) 从内存地址为 中断类型码*4 和 中断类型码*4+2 的两个字单元中读取中断处理程序的入口地址设置IP和CS。



描述中断过程:

  1) 取得中断类型码N
  2) pushf
  3) TF=0 ,  IF=0
  4) push CS
  5) push IP
  6) (IP)=(N*4), (CS)=(N*4+2)



中断处理程序


CPU随时都可能检测到中断信息,所以中断处理程序必须一直存储在内存某段空间之中。

中断处理程序的入口地址(中断向量)必须存储在对应的中断向量表项中。


中断处理程序的编写步骤:

1) 保存用到的寄存器
2) 处理中断
3) 恢复用到的寄存器
4) 用 iret 指令返回


iret 指令的功能用汇编语法描述为:

  pop  IP
  pop  CS
  popf

iret 通常和硬件自动完成的中断过程配合使用。
iret指令执行后,CPU回到执行中断处理程序前的执行点继续执行程序。


除法错误中断的处理


CPU执行div等除法指令时,如发生了除法溢出错误,将产生中断类型码为0的中断信息。CPU检测到这个信息,引发中断过程。


编程处理0号中断


编程:当发生了除法溢出错误产生0号中断信息,引发中断过程,CPU执行我们编写的0号中断处理程序。
   在屏幕中间显示提示信息: “overflow!” 后,返回到操作系统中。


编写中断处理程序,当中断0发生时:

  1) 相关处理
  2) 向显示缓冲区送字符串 "overflow!"
  3) 返回DOS


8086支持256个中断,实际系统中要处理的中断事件没有达到256个,在中断向量表中有些单元是空的。

从 0000:0200 至 0000:0300 的256个字节的空间所对应的中断向量表项都是空的,操作系统和其他应用程序都不占用。

可以将自己写的中断处理程序扔到内存 0000:0200 处。那么 0000:0200 就是中断入口地址,把它登记在中断向量表的对应表项中。
-d 0:0200
0000:0200  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0210  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0230  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0240  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0250  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0260  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0000:0270  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
……
步骤:

1) 编写可以显示 “overflow!” 的中断处理程序
2) 将程序送入内存 0000:0200 处
3) 将程序的入口地址 0000:0200 存储在中断向量表0号表项中
              assume    cs:code
        code  segment
      start:  mov       ax,cs
              mov       ds,ax       ; 源
              mov       si,offset do0
              mov       ax,0
              mov       es,ax       ; 目标
              mov       di,200h
              mov       cx,offset do0end - offset do0
              cld
              rep       movsb
              mov       ax,0        ; 设置中断向量
              mov       es,ax
              mov       word ptr es:[0*4],200h
              mov       word ptr es:[0*4+2],0
              mov       ax,4c00h
              int       21h

        do0:  jmp       short do0start
              db        "overflow!"
   do0start:  mov       ax,cs
              mov       ds,ax
              mov       si,202h     ; ds:si指向字符串
              mov       ax,0b800h
              mov       es,ax
              mov       di,12*160+36*2
              mov       cx,9
          s:  mov       al,[si]
              mov       es:[di],al
              inc       si
              add       di,2
              loop      s
              mov       ax,4c00h
              int       21h
     do0end:  nop
        code  ends
              end       start
CMD 不支持直接写屏操作,DOS下支持~:)


当发生除法溢出时,那个字符串就跳了出来,然后返回到dos。

  Quote:
C:\Masm50>debug
-a
0AF5:0100 mov ax,1000
0AF5:0103 mov bl,1
0AF5:0105 div bl
0AF5:0107
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80010        MOV     AX,1000
-t
                                    overflow!
AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 B301          MOV     BL,01
-t

AX=1000  BX=0001  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0105   NV UP EI PL NZ NA PO NC
0AF5:0105 F6F3          DIV     BL
-t

单步中断

CPU在执行完一条指令后,如果检测到标志寄存器的TF位为1,则产生单步中断,引发中断过程。

单步中断的中断类型码为1,引发的中断过程如下:

  1) 取得中断类型码1
  2) 标志寄存器入栈,TF、IF设置为0
  3) CS、IP入栈
  4) (ip)=(1*4) , (cs)=(1*4+2)

如果TF=1,则执行一条指令后,CPU就会转去执行1号中断处理程序。

Debug 工具也是这样单步跟踪的原理。

CPU提供单中断功能的原因:为单步跟踪程序的执行过程提供了实现机制。



响应中断的特殊情况
0AF5:0100 mov ax,1000
0AF5:0103 mov ss,ax
0AF5:0105 mov ax,0
0AF5:0108 mov sp,0
0AF5:010B
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 B80010        MOV     AX,1000
-t

AX=1000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0103   NV UP EI PL NZ NA PO NC
0AF5:0103 8ED0          MOV     SS,AX
-t

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=1000  CS=0AF5  IP=0108   NV UP EI PL NZ NA PO NC
0AF5:0108 BC0000        MOV     SP,0000
-t

AX=0000  BX=0000  CX=0000  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=1000  CS=0AF5  IP=010B   NV UP EI PL NZ NA PO NC
编写0号中断的处理程序

在除法溢出发生时,在屏幕中间显示字符串 "divide rror!",然后返回到DOS。
              assume    cs:code

        code  segment
      start:  mov       ax,cs
              mov       ds,ax
              mov       si,offset do0
              mov       ax,0h
              mov       es,ax
              mov       di,200h
              mov       cx,offset doend - offset do0
              cld
              rep       movsb
              mov       ax,0h
              mov       es,ax
              mov       word ptr es:[0*4],200h  ; 0:200 Int 0
              mov       word ptr es:[0*4+2],0h
              mov       ax,4c00h
              int       21h

        do0:  jmp       short do1
              db        "my name is redtek",0
        do1:  mov       ax,cs
              mov       ds,ax
              mov       si,202h
              mov       ax,0b800h
              mov       es,ax
              mov       di,160*20+(160-18*2)/2
              mov       cx,18
              mov       ah,02h
          s:  mov       al,[si]
              mov       es:[di],ax
              inc       si
              add       di,2
              loop      s
              mov       ax,4c00h
              int       21h
      doend:  nop

        code  ends
              end       start
修改中断向量前、后对比:

  Quote:
C:\Masm50>debug
-d 0:0
0000:0000  68 10 A7 00 8B 01 70 00-16 00 A9 03 8B 01 70 00   h.....p.......p.
0000:0010  8B 01 70 00 B9 06 12 02-40 07 12 02 FF 03 12 02   ..p.....@.......
0000:0020  46 07 12 02 0A 04 12 02-3A 00 A9 03 54 00 A9 03   F.......:...T...
0000:0030  6E 00 A9 03 88 00 A9 03-A2 00 A9 03 FF 03 12 02   n...............
0000:0040  A9 08 12 02 A4 09 12 02-AA 09 12 02 5D 04 12 02   ............]...
0000:0050  B0 09 12 02 0D 02 E1 02-C4 09 12 02 8B 05 12 02   ................
0000:0060  0E 0C 12 02 14 0C 12 02-1F 0C 12 02 AD 06 12 02   ................
0000:0070  AD 06 12 02 A4 F0 00 F0-37 05 12 02 94 2D 00 C0   ........7....-..
-q

C:\Masm50>p240.exe

C:\Masm50>debug
-d 0:0
0000:0000  00 02 00 00 8B 01 70 00-16 00 A9 03 8B 01 70 00   ......p.......p.
0000:0010  8B 01 70 00 B9 06 12 02-40 07 12 02 FF 03 12 02   ..p.....@.......
0000:0020  46 07 12 02 0A 04 12 02-3A 00 A9 03 54 00 A9 03   F.......:...T...
0000:0030  6E 00 A9 03 88 00 A9 03-A2 00 A9 03 FF 03 12 02   n...............
0000:0040  A9 08 12 02 A4 09 12 02-AA 09 12 02 5D 04 12 02   ............]...
0000:0050  B0 09 12 02 0D 02 E1 02-C4 09 12 02 8B 05 12 02   ................
0000:0060  0E 0C 12 02 14 0C 12 02-1F 0C 12 02 AD 06 12 02   ................
0000:0070  AD 06 12 02 A4 F0 00 F0-37 05 12 02 94 2D 00 C0   ........7....-..

[ Last edited by redtek on 2007-1-9 at 12:26 AM ]
附件 1: 1.GIF (2007-1-9 13:26, 7.25 K, 下载附件所需积分 1点 ,下载次数: 1)



作者: redtek     时间: 2007-1-9 23:32    标题: 【80x86汇编语言学习笔记】

Int 指令


格式: int n
相当于引发一个n号中断的中断过程。


1) 取中断类型码n
2) 标志寄存器入栈,IF=0,TF=0
3) CS、IP入栈
4) (IP)=(n*4), (CS)=(n*4+2)


先运行自编的0号中断程序(安装)并修改中断向量表。(位于92楼 P240.EXE)
然后调用 int 0 引发0号中断:
C:\Masm50>debug
-a
0AF5:0100 int 0
0AF5:0102
-g

C:\Masm50>
CPU执行 int 0 指令时,将引发中断过程,执行0号中断处理程序。
而0号中断处理程序的功能就是显示一行字符串(如上:92楼0号中断处理程序)

int 指令的最终功能和call指令相似,都是调用一段程序。



编写供应用程序调用的中断例程


编写、安装中断7ch的中断例程,功能:求一word型数据的平方。

参数:(AX)=要计算的数据
返回:DX、AX中存放结果的高16位和低16位。

求:2*3456^2
              assume    cs:code
        code  segment
      start:  mov       ax,3456
              int       7ch         ; 调用中断 7ch 的中断例程,计算ax中的数据的平方
              add       ax,ax       ; 低位相加
              adc       dx,dx       ; 再加高位和进位
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
1) 编写实现平方功能程序
2) 安装程序
3) 设置中断向量表,指定入口地址

安装程序
              assume    cs:code
        code  segment
      start:  mov       ax,cs       ; 安装代码
              mov       ds,ax
              mov       si,offset sqr
              mov       ax,0
              mov       es,ax
              mov       di,200h
              mov       cx,offset sqrend - offset sqr
              cld
              rep       movsb
              mov       ax,0
              mov       es,ax
              mov       word ptr es:[7ch*4],200h
              mov       word ptr es:[7ch*4+2],0
              mov       ax,4c00h
              int       21h

        sqr:  mul       ax          ; 中断7ch例程
              iret
     sqrend:  nop
        code  ends
              end       start
执行过程验证

  Quote:
C:\Masm50>debug p242.exe
-r
AX=0000  BX=0000  CX=000E  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 B8800D        MOV     AX,0D80
-u
0B49:0000 B8800D        MOV     AX,0D80
0B49:0003 CD7C          INT     7C
0B49:0005 03C0          ADD     AX,AX
0B49:0007 13D2          ADC     DX,DX
0B49:0009 B8004C        MOV     AX,4C00
0B49:000C CD21          INT     21
0B49:000E E270          LOOP    0080
0B49:0010 83C406        ADD     SP,+06
0B49:0013 B8C805        MOV     AX,05C8
0B49:0016 50            PUSH    AX
0B49:0017 8D4680        LEA     AX,[BP-80]
0B49:001A 50            PUSH    AX
0B49:001B E83E0D        CALL    0D5C
0B49:001E 83C404        ADD     SP,+04
-t

AX=0D80  BX=0000  CX=000E  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0003   NV UP EI PL NZ NA PO NC
0B49:0003 CD7C          INT     7C
-p

AX=4000  BX=0000  CX=000E  DX=00B6  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0005   NV UP EI PL NZ NA PO NC
0B49:0005 03C0          ADD     AX,AX
-t

AX=8000  BX=0000  CX=000E  DX=00B6  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0007   OV UP EI NG NZ NA PE NC
0B49:0007 13D2          ADC     DX,DX
-t

AX=8000  BX=0000  CX=000E  DX=016C  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0009   NV UP EI PL NZ NA PE NC

CPU执行 int 7ch 指令进入中断例程之前,标志寄存器、当前的CS和IP被压入栈中,在执行完中断例程后,用 iert 指令恢复 int 7ch 执行前的标志寄存器和CS、IP的值。

int 指令和 iret 指令的配合使用与 call 指令和 ret 指令的配合使用具有相似的思路。



编写、安装中断7ch的中断例程。

功能:将一个全是字母,以0结尾的字符串转化为大写。
参数:ds:si指向字符串的首地址
              assume    cs:code
        data  segment
              db        'conversation',0
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       si,0
              int       7ch         ; 调用字符串转大写的中断例程
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
7ch 中断例程安装程序
              assume    cs:code
        code  segment
      start:  mov       ax,cs
              mov       ds,ax
              mov       si,offset capital
              mov       ax,0
              mov       es,ax
              mov       di,200h
              mov       cx,offset capitalend - offset capital
              cld
              rep       movsb
              mov       ax,0
              mov       es,ax
              mov       word ptr es:[7ch*4],200h
              mov       word ptr es:[7ch*4+2],0
              mov       ax,4c00h
              int       21h

    capital:  push      cx          ; 保存寄存器值,避免冲突
              push      si
     change:  mov       cl,[si]
              mov       ch,0
              jcxz      ok          ; 遇到字符串尾0,则转向结束
              and       byte ptr [si],11011111b
              inc       si
              jmp       short change
         ok:  pop       si
              pop       cx
              iret
capitalend:  nop
        code  ends
              end       start
跟踪结果

  Quote:
-t

AX=0B49  BX=0000  CX=001F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B4A  IP=0003   NV UP EI PL NZ NA PO NC
0B4A:0003 8ED8          MOV     DS,AX
-t

AX=0B49  BX=0000  CX=001F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B49  ES=0B39  SS=0B49  CS=0B4A  IP=0005   NV UP EI PL NZ NA PO NC
0B4A:0005 BE0000        MOV     SI,0000
-t

AX=0B49  BX=0000  CX=001F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B49  ES=0B39  SS=0B49  CS=0B4A  IP=0008   NV UP EI PL NZ NA PO NC
0B4A:0008 CD7C          INT     7C
-p

AX=0B49  BX=0000  CX=001F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B49  ES=0B39  SS=0B49  CS=0B4A  IP=000A   NV UP EI PL NZ NA PO NC
0B4A:000A B8004C        MOV     AX,4C00
-d ds:0
0B49:0000  43 4F 4E 56 45 52 53 41-54 49 4F 4E 00 00 00 00   CONVERSATION....
0B49:0010  B8 49 0B 8E D8 BE 00 00-CD 7C B8 00 4C CD 21 C4   .I.......|..L.!.
0B49:0020  04 C7 86 FE FE 00 00 EB-05 90 FF 86 FE FE A1 56   ...............V
0B49:0030  07 39 86 FE FE 73 7D 8B-9E FE FE D1 E3 D1 E3 8B   .9...s}.........
0B49:0040  87 BE 22 0B 87 C0 22 74-E1 8B 9E FE FE D1 E3 D1   .."..."t........
0B49:0050  E3 8B 87 BE 22 8B 97 C0-22 89 86 FA FE 89 96 FC   ...."...".......
0B49:0060  FE C4 9E FA FE 26 8A 47-0C 2A E4 40 50 8B C3 05   .....&.G.*.@P...
0B49:0070  0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50   ..RP..F...P....P

[ Last edited by redtek on 2007-1-9 at 12:05 PM ]
作者: redtek     时间: 2007-1-10 07:38    标题: 【80x86汇编语言学习笔记】

对 int、iret和栈的深入理解




) 用 7ch 中断例程完成 jmp near ptr s 指令的功能,用bx向中断例程传送转移位移。

  在屏幕的第 12 行显示 data 段中,以 0 结尾的字符串。



调用者:
              assume    cs:code
        data  segment
              db        'conversation',0
        data  ends
        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       si,0
              mov       ax,0b800h
              mov       es,ax
              mov       di,160*12

          s:  cmp       byte ptr [si],0
              je        ok
              mov       al,[si]
              mov       es:[di],al
              mov       es:[di].1,al
              inc       si
              add       di,2
              mov       bx,offset s - offset ok
              int       7ch
         ok:  mov       ax,4c00h
              int       21h
        code  ends
              end       start
中断例程:
              assume    cs:code
        code  segment
      start:  mov       ax,cs
              mov       ds,ax
              mov       si,offset do0
              mov       ax,0
              mov       es,ax
              mov       di,200h
              mov       cx,offset doend - offset do0
              cld
              rep       movsb
              mov       ax,0
              mov       es,ax
              mov       word ptr es:[7ch*4],200h
              mov       word ptr es:[7ch*4+2],0
              mov       ax,4c00h
              int       21h

        do0:  push      bp
              mov       bp,sp
              add       [bp+2],bx   ; 设置IP位移量
              pop       bp
              iret
      doend:  nop

        code  ends
              end       start
) 编程:用7ch中断例程完成loop指令的功能。
  在屏幕中间显示80个‘!’。
              assume    cs:code
        code  segment
      start:  mov       ax,0b800h
              mov       es,ax
              mov       di,160*12
              mov       bx,offset s - offset se ; 存储模拟loop的转移位移
          s:  mov       byte ptr es:[di],'!'
              add       di,2
              int       7ch
         se:  nop

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
进入中断处理程序之前的压栈次序:

  标志寄存器入栈,(TF=0、IF=0);
  CS入栈
  IP入栈
  (IP)=(N*4), (CS)=(N*4+2)
  ------------------------------------------------------- 16位

  最后,栈指针(SP)指向最后入栈的16位内容所在的地址。


7ch中断例程……当CPU进行一系列动作时,其压入IP的内容并不是在调用程序中的 int 7ch 所在的IP。
根据CPU执行指令的原理,当CPU读入IP所指向地址的指令时,CPU会另IP指向一下条指令。

所以,当调用 int 7ch 中断之前,CPU 所压入的 IP 是指向一下条指令的,是为了准备执行完当前 int 指令后返回继续执

行之备。

所以,被压入栈的 IP 就是调用代码中“se:  nop”这行 se 标号所在的地址~:)
而且,当要 pop 弹出时,则应逆序要弹出的也是 IP ,所以进入 int 7ch 中断处理程序后,其 SP 一直指向 IP 。

当执行 7ch 中断例程时,调用者已将从 se 到 s 标号的位移(使用 offset 指令计算的)存到了 bx 中。

然后将 CS:IP 指向 s 标号(就是将 CS 指向 s 所在的段地址,让 IP 指向 S 所在的偏移地址就行了)。
当 int 7ch 中断例程内,将栈中所压的 IP 与 CS 值改写以后,当执行 iret 指令时,CPU会自动恢复中断前的寄存器

的值,但这值已被我们修改,所以恢复的就是我们修改过的 CS:IP(它指向了 s 标号处)。


下面是调试出错的代码,当 dec cx (递减)时,cx=0 时再递减就发生了为负值,结果进入了死循环。

  Quote:
AX=B800  BX=FFF7  CX=0000  DX=0000  SP=FFFA  BP=0000  SI=0000  DI=078A
DS=0B39  ES=B800  SS=0B49  CS=0000  IP=0200   NV UP DI PL NZ NA PO NC
0000:0200 55            PUSH    BP
-t

AX=B800  BX=FFF7  CX=0000  DX=0000  SP=FFF8  BP=0000  SI=0000  DI=078A
DS=0B39  ES=B800  SS=0B49  CS=0000  IP=0201   NV UP DI PL NZ NA PO NC
0000:0201 8BEC          MOV     BP,SP
-t

AX=B800  BX=FFF7  CX=0000  DX=0000  SP=FFF8  BP=FFF8  SI=0000  DI=078A
DS=0B39  ES=B800  SS=0B49  CS=0000  IP=0203   NV UP DI PL NZ NA PO NC
0000:0203 49            DEC     CX
-t

AX=B800  BX=FFF7  CX=FFFF  DX=0000  SP=FFF8  BP=FFF8  SI=0000  DI=078A
DS=0B39  ES=B800  SS=0B49  CS=0000  IP=0204   NV UP DI NG NZ AC PE NC
0000:0204 E304          JCXZ    020A

原来在 int 7ch 中多写了一个 dec cx ,jczx 的判断为0语句被夹在中间,这样它永远也不能正确。


调用者:
              assume    cs:code
        code  segment
      start:  mov       ax,0b800h
              mov       es,ax
              mov       di,160*12
              mov       bx,offset s - offset se ; 模拟loop的转移位移
              mov       cx,80
          s:  mov       byte ptr es:[di],'!'
              mov       byte ptr es:[di].1,cl   ; 变换不同颜色
              add       di,2
              int       7ch
         se:  nop

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
7ch 中断例程安装
              assume    cs:code
        code  segment
      start:  mov       ax,cs       ; int 7ch 例程安装
              mov       ds,ax
              mov       si,offset do0
              mov       ax,0
              mov       es,ax
              mov       di,200h
              mov       cx,offset doend - offset do0
              cld
              rep       movsb
              mov       ax,0
              mov       es,ax
              mov       word ptr es:[7ch*4],200h
              mov       word ptr es:[7ch*4+2],0
              mov       ax,4c00h
              int       21h

        do0:  push      bp          ; int 7ch 中断例程
              mov       bp,sp
              dec       cx
              jcxz      lpret       ; 当 cx 值递减至0时结束,准备返回调用者
              add       [bp+2],bx   ; 修改 IP 位移
                                    ; 调用者 se + bx中与 s标号处之间的位移,就是s的偏移量
                                    ; 因为栈中又压入了 bp(占16位,2字节),所以+2指向栈底方向的IP
      lpret:  pop       bp
              iret
      doend:  nop

        code  ends
              end       start
) 上面的内容中,用 7ch 中断例程实现 loop 的功能,上面的 7ch 中断例程所能进行的最大转移位移是多少?
  
  因为 loop 位移是 short 型段内短转移指令。
  它的位移量是有符号数,最大位移范围是: -128 ~~ + 127 



运行界面

[ Last edited by redtek on 2007-1-9 at 09:46 PM ]
附件 1: 1.GIF (2007-1-10 07:38, 11.38 K, 下载附件所需积分 1点 ,下载次数: 1)



作者: redtek     时间: 2007-1-10 10:50    标题: 【80x86汇编语言学习笔记】

BIOS和DOS所提供的中断例程


在系统板的ROM中存放着一套程序,称为BIOS(基本输入输出系统),BIOS中主要包括:

1) 硬件系统的检测和初始化程序
2) 外部中断和内部中断的中断例程
3) 用于对硬件设备进行 I/O 操作的中断例程
4) 其他和硬件系统相关的中断例程。


操作系统DOS也提供了中断例程。
从操作系统角度来看DOS的中断例程就是操作系统向程序员提供的编程资源。


BIOS和DOS所有提供的中断例程中包含了许多子程序。程序员在编程的时候可以用int指令直接调用中断例程来完成工作。


BIOS和DOS中断例程的安装过程:

1) 开机后,CPU加电,初始化(CS)=0FFFFH,(IP)=0。
   自动从FFFF:0单元开始执行程序。
   FFFF:0处有一条跳转指令,CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。

2) 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。
   对于BIOS所提供的中断例程,只需将入口地址登记在中断向量表中即可,因它是固化到ROM中的程序,一直在内存中。

3) 硬件系统检测和初始化完成后,调用 int 19h 进行操作系统的引导。从此将计算机交由操作系统控制。

4) DOS启动后,除完成其他工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量。



BIOS中断例程应用

一个供程序调用的中断例程中往往包括多个子程序,中断例程内部用传递进来的参数来决定执行哪一个子程序。


BIOS和DOS提供的中断程序,都用AH来传递内部子程序的编号。


int 10h 中断例程设置光标位置
              assume    cs:code
        code  segment
      start:  mov       ah,2        ; 调用10号中断例程2号子程序: 置光标位置
              mov       bh,0        ; 0页
              mov       dh,5        ; 行号
              mov       dl,12       ; 列号
              int       10h
              int       20          ; 行: 0~24  列: 0~79

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
置光标位置用在批处理里,可以将光标定位在屏幕的任何地方。
再使用 SET /P=要显示的文字 <NUL 就可以完成类似“填表格”等特殊显示应用。

关于BIOS或是DOS功能调用专门有手册可以查阅,除特殊情况基本都不用记背。

bh 中的页号的含义,显示缓冲区分为8页。(80*25彩色字符模式)每页 160每行 * 25行 = 4000 字节。


int 10h 中断例程在当前光标位置处写属性和字符
              assume    cs:code
        code  segment
      start:  mov       ah,9        ; 调用10h号中断例程9号子程序: 在当前光标位置处写属性和字符
              mov       al,'a'      ; AL=要显示的字符; BH=页号; BL=字符属性; CX=写字符个数
              mov       bh,0
              mov       bl,26
              mov       cx,3
              int       10h
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
在屏幕的5行12列显示红底高亮闪烁(cmd全屏下闪烁无效)绿色的'a'。
              assume    cs:code
        code  segment
      start:  mov       ah,2
              mov       bh,0
              mov       dh,5
              mov       dl,12
              int       10h

              mov       ah,9
              mov       al,'a'
              mov       bl,11001010b
              mov       bh,0
              mov       cx,10
              int       10h

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
颜色属性

  Quote:
7          6    5     4       3             2     1    0
---------------------------------------------------------------
BL  R G B  I    R G B
------      --------------       -----          ---------------
闪烁  背   景  高亮   前   景

R:红色  G:绿色  B:蓝色

DOS中断例程应用


int 21h中断例程是DOS提供的中断例程,其中包含了DOS提供给程序员在编程时调用的子程序。

int 21h 中断例程 4ch 号功能,结束程序(带返回码结束)。
              mov       ah,4ch
              mov       al,0
              int       21h
int 21h中断例程在光标位置显示字符串功能


在屏幕的5行12列显示字符串:“Welcome to masm”。
              assume    cs:code

        data  segment
              db        'Welcome to masm','$'
        data  ends

        code  segment
      start:  mov       ah,2        ; To 光标:5,12列
              mov       bh,0
              mov       dh,5
              mov       dl,12
              int       10h

              mov       ax,data
              mov       ds,ax
              mov       dx,0
              mov       ah,9
              int       21h

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
5行(是指从0~5),12例(是指从0~12)
$ 本身不显示,起边界作用。



编写应用中断例程


编写并安装 int 7ch 中断例程,功能为显示一个用0结束的字符串,中断例程安装在0:200处。

参数:(DH)=行号,(DL)=列号,(CL)=颜色,DS:SI指向字符串首地址。


inc 7ch 安装程序
              assume    cs:code
        code  segment
      start:  mov       ax,cs       ; 安装
              mov       ds,ax
              mov       si,offset do0
              mov       ax,0
              mov       es,ax
              mov       di,200h
              mov       cx,offset doend - offset do0
              cld
              rep       movsb
              mov       ax,0
              mov       es,ax
              mov       word ptr es:[7ch*4],200h
              mov       word ptr es:[7ch*4+2],0
              mov       ax,4c00h
              int       21h


        do0:  push      dx          ; int 7ch
              push      cx          ; 参数:(DH)=行号,(DL)=列号,(CL)=颜色
              push      ax
              push      di
              push      es
              mov       si,0
              mov       ax,0b800h
              mov       es,ax
              mov       ax,160
              mul       dh
              mov       di,ax
              mov       dh,0
              add       dx,dx
              add       di,dx
          s:  mov       ch,[si]
              cmp       ch,0
              je        send
              mov       es:[di],ch
              mov       es:[di].1,cl
              inc       si
              add       di,2
              jmp       short s
       send:  pop       es
              pop       di
              pop       ax
              pop       cx
              pop       dx
              iret
      doend:  nop
        code  ends
              end       start
调用者
              assume    cs:code
        data  segment
              db        "welcome to masm!",0
        data  ends

        code  segment
      start:  mov       dh,10       ; 行号
              mov       dl,10       ; 列号
              mov       cl,2        ; 颜色
              mov       ax,data
              mov       ds,ax
              mov       si,0
              int       7ch
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
Debug 跟踪观察 int、iret指令执行前后CS、IP和栈中的状态。



编写并安装 int 7ch 中断例程,功能为完成 loop 指令的功能。

参数:(CX)=循环次数, (BX)=位移

在屏幕中间显示80个‘!’。

调用者:
              assume    cs:code
        code  segment
      start:  mov       ax,0b800h
              mov       es,ax
              mov       di,160*12
              mov       bx,offset s - offset se
              mov       cx,80
          s:  mov       byte ptr es:[di],'!'
              add       di,2
              int       7ch
         se:  nop
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
int 7ch 安装
              assume    cs:code
        code  segment
      start:  mov       ax,cs
              mov       ds,ax
              mov       si, offset do0
              mov       ax,0
              mov       es,ax
              mov       di,200h
              mov       cx,offset doend - offset do0
              cld
              rep       movsb
              mov       word ptr es:[7ch*4],200h
              mov       word ptr es:[7ch*4+2],0
              mov       ax,4c00h
              int       21h

        do0:  push      bp
              mov       bp,sp
              dec       cx
              jcxz      doreturn
              add       [bp+2],bx
   doreturn:  pop       bp
              iret
      doend:  nop

        code  ends
              end       start
需要注意的地方

当使用 BP 寄存器时寻址时,所操作的是 SS:[  ]
当使用非 BP 寄存器寻址时,所操作的默认为 DS:[ ] 或指定。

ADD     [BP+02],BX

  Quote:
AX=B800  BX=FFF7  CX=004F  DX=0000  SP=FFF8  BP=FFF8  SI=0000  DI=0782
DS=0B39  ES=B800  SS=0B49  CS=0000  IP=0206   NV UP DI PL NZ AC PO NC
0000:0206 015E02        ADD     [BP+02],BX                         SS:FFFA=0017


-d ss:fffa
0B49:FFF0                                17 00 49 0B 06 32             ..I..2

分别在屏幕的第2、4、6、8行显示四句英文诗,补全程序

  Quote:
              assume    cs:code
        code  segment
         s1:  db        'Good,btter,best,','$'
         s2:  db        'Never let it rest,','$'
         s3:  db        'Till good is better,','$'
         s4:  db        'And better,best.','$'
          s:  dw        offset s1,offset s2,offset s3,offset s4
        row:  db        2,4,6,8

      start:  mov       ax,cs
              mov       ds,ax
              mov       bx,offset s
              mov       si,offset row
              xor       ax,ax
              mov       cx,4
         ok:  mov       bh,0
              mov       dh,[si]     ; 行号
              mov       dl,0        ; 0列
              mov       ah,2
              int       10h

              mov       dx,[bx]
              mov       ah,9
              int       21h


              inc       si          ; 下移指针
              add       bx,2
              loop      ok
              mov       ax,4c00h
              int       21h
        code  ends
              end       start

[ Last edited by redtek on 2007-1-10 at 04:24 PM ]
作者: pengfei     时间: 2007-1-10 11:42


  Quote:
Originally posted by redtek at 2006-12-10 00:48:
【80x86 汇编语言学习笔记】 —— 一句话学习总结


) 找一本认为最难的书学习,更是为了总结最适合自己的学习方法。
  只有读那些看上去 “最难的” 书,才最能挖掘更适合自己的有效学习规律与方法。

) 读看一本很难读的书,才最容易看到自己思想上与能力上的弱点。

) 学与悟的重点放在书上所讲的原理,就等于拥有了创造一万种方法的灵感与能力。

) 无论是解读汇编语言还是解读任何一本书,其实都是在悟读着自己。

) 某个阶段,读到认为掌握了很多,那就是并没有学透多少;读到好象什么都不会了,那是已经读透了部分,要再继续。

) 读适合启发自己思维方式的书,读这样的书才能加倍成长。

) 基础知识就是巨厦的地基,它的深浅有一半注定了最终的计算机水平,它的另一半是对数学的理解。

) 跟着书读是白读。先拉后跟的读是真读。用自己的思考方式一边超前预想一边读,会把见解与书的思想碰撞,这才是读。

) 学习某种编程语言的过程,如果发现只有死记硬背才能继续学下去,那么这个时候就意味着基础没有掌握好,应去补基础。

) 把需要死记硬背才能向下学习的过程,分解成不需要记忆照样可以学下去的过程,那就是掌握它的原理深度来降低记忆强度。

看了兄写的这段学习总结, 感慨良多, 每一点都命中要害.

以前我读书吊儿郎当, 很容易满足, 自学的一年半年里慢慢地找到了学习的方法, 更重要的是磨练了自己的意志, 如果能够做到为了掌握一个知识点连续作战十几个小时, 还有什么困难可以难倒你.

Redtek兄说的对, 学习重在理解, 做事做人都在于领悟, 也许苦学几载不如一朝顿悟.

过去一失败就垂头丧气, 现在觉得失败或许也是成功的预兆, 要善于失败之后找出自己的不足, 设法弥补它.

思想是重要的, 做任何事情, 自己的决心和态度往往是成败的关键.

开弓没有回头箭, 坚持下去你就是最强的, 最后送兄一句:

得意失意, 切莫在意.  顺境逆境, 切莫止境!
作者: GOTOmsdos     时间: 2007-1-10 12:01
支持!
我学C的情景跟redtek兄学汇编差不多,千军万马中冲出一条血路!
作者: redtek     时间: 2007-1-10 22:17
“得意失意, 切莫在意.  顺境逆境, 切莫止境! ”,欣赏~~~

听pengfei兄讲学习感受和GOTOmsdos兄的鼓励真是受益非浅~:)))
作者: redtek     时间: 2007-1-11 05:30    标题: 【80x86汇编语言学习笔记】

端口


各种存府器都和CPU的地址线、数据线、控制线相连。
CPU在操控它们的时候,把它们都当作内存来对待,把它们总的看做一个由若干存储单元组成的逻辑存储器,称其为内存地址空间。



PC机系统中,和CPU通过总线相连的芯片除各种存储器外,还有以下3种芯片:

1) 各种接口卡(网卡、显卡、……)上的接口芯片,它们控制接口卡进行工作
2) 主板上的接口芯片,CPU通过它们对部分外设进行访问
3) 其他芯片,用来存储相关的信息,或进行相关的输入输出处理


从CPU角度,将这些寄存器都当作端口,对它们进行统一编址,从而建立了一个统一端口地址空间。每个端口在地址空间中都有一个地址。


CPU可以直接读写3个地方的数据:

1) CPU内部的寄存器
2) 内存单元
3) 端口



端口的读写


访问端口时CPU通过端口地址来定位端口,端口所在芯片和CPU通过总线相连。端口地址和内存地址一样,通过地址总线来传送。

CPU最多可以定位64K个不同的端口。端口地址的范围为: 0~65535。

in 和 out。



访问端口: 

in al,60h         ; 从60h号端口读入一个字节


执行时与总线相关的操作:

1) CPU通过地址线将地址信息60H发出
2) CPU通过控制线发出端口读命令,选中端口所在的芯片,并通知它,将要从中读取数据
3) 端口所在的芯片将60H端口中的数据通过数据线送入CPU



在in和out指令中,只能使用ax或al来存放从端口中读入的数据或要发送到端口中的数据。

访问8位端口时用al,访问16位端口时用ax。



对0~255以内的端口进行读写时:

in  al,20h   ; 从20H端口读入一个字节
out 20h,al   ; 往20H端口写入一个字节


对256~65535的端口进行读写时,端口号放在dx中:

mov dx,3f8h  ; 将端口号3F8H送入dx
in  al,dx    ; 从3F8H读入一个字节
out dx,al    ; 向3F8H端口写入一个字节



CMOS RAM芯片

0~0DH单元保存时间信息,其余大部分用于保存系统配置信息,供系统启动时BIOS程序读取。

芯片内部有两个端口,端口地址为70H和71H。CPU通过这两个端口写CMOS RAM。

70H为地址端口,存放要访问的CMOS RAM单元地址
71H为数据端口,存放从选定的CMOS RAM单元中读取的数据。

CPU对CMOS RAM的读写分两步进行。

例:读CMOS RAM的2号单元:

  1) 将2送入端口70H
  2) 从71H读出2号单元的内容


读取CMOS RAM的2号单元内容
              mov       al,2
              out       70h,al
              in        al,71h
向CMOS RAM的2号单元写入0
C:\Masm50>debug
-a
0AF5:0100 mov al,2
0AF5:0102 out 70,al
0AF5:0104 mov al,0
0AF5:0106 out 71,al
CMOS RAM中存储的时间信息


CMOS RAM 各字节的含义(部分):

===============================================================
偏移值(Offset)    数据字段的意义描述(Description)
---------------------------------------------------------------
00h               目前系统时间的“秒数”字段
01h               预约警铃时间的“秒数”字段
02h               目前系统时间的“分钟”字段
03h               预约警铃时间的“分钟”字段
04h               目前系统时间的“小时”字段
05h               预约警铃时间的“小时”字段
06h               星期几(星期一=01,星期二=02,依次类推)
07h               目前系统日期字段(0~31)
08h               系统公元纪年的后两位(00~99;00=2000,01=2001,以此类推)



(关于上面BIOS更详细的内容,传说中有一本书,研读完了可以自己开发BIOS,哈哈……)

(关于这本书的介绍:   《BIOS研发技术剖析》 )

  Quote:
【内容简介】
本书是全球第一本针对新一代计算机平台的BIOS核心技术做深入剖析的技术性书籍,内附完整的BIOS源代码以及相关的技术文献,完备程度超过二十年前那本IBM BIOS Bible,将这个业界研发技术缺口至少拉近了十八、九年,所以本书的出现,绝对可以说是难得的创举! 本书不仅着重于BIOS的结构剖析,还指导读者如何制作及修改BIOS,更难能可贵的是本书附赠了最重要的源程序代码,让读者能实际动手做BIOS。本书主要针对具有基本汇编语言基础,熟悉DOS操作指令且从事软件开发、编程的读者编写而成。  

索引CMOS内容对照(摘自互联网,原作者不详)

  Quote:
CMOS内容对照
地址 内容 地址 内容 地址 内容 地址 内容
00h Time - Seconds 20h Reserved 40h Extended CMOS 60h User Password
01h Alarm - Seconds 21h Reserved 41h Extended CMOS 61h User Password
02h Time - Minutes 22h Reserved 42h Extended CMOS 62h Extended CMOS
03h Alarm - Minutes 23h Reserved 43h Extended CMOS 63h Extended CMOS
04h Time - Hours 24h Reserved 44h Extended CMOS 64h Extended CMOS
05h Alarm - Hours 25h Reserved 45h Extended CMOS 65h Extended CMOS
06h Date - Day of the week 26h Reserved 46h Extended CMOS 66h Extended CMOS
07h Date - Day 27h Reserved 47h Extended CMOS 67h Extended CMOS
08h Date - Month 28h Reserved 48h Extended CMOS 68h Extended CMOS
09h Date - Year 29h Reserved 49h Extended CMOS 69h Extended CMOS
0Ah Status Register A 2Ah Reserved 4Ah Extended CMOS 6Ah Extended CMOS
0Bh Status Register B 2Bh Reserved 4Bh Extended CMOS 6Bh Extended CMOS
0Ch Status Register C 2Ch Reserved 4Ch Extended CMOS 6Ch Extended CMOS
0Dh Status Register D 2Dh Reserved 4Dh Extended CMOS 6Dh Extended CMOS
0Eh Diagnostic Status 2Eh CMOS Checksum (high byte) 4Eh Extended CMOS 6Eh Extended CMOS
0Fh Shutdown Status 2Fh CMOS Checksum (low byte) 4Fh Extended CMOS 6Fh Extended CMOS
10h A; 30h Extended Memory (high byte) 50h Extended CMOS 70h Extended CMOS
11h Reserved 31h Extended Memory (low byte) 51h Extended CMOS 71h Extended CMOS
12h 0 32h Date - Century 52h Extended CMOS 72h Extended CMOS
13h Reserved 33h Power On Status 53h Extended CMOS 73h Extended CMOS
14h Equipment Installed 34h Reserved 54h Extended CMOS 74h Extended CMOS
15h Base Memory (high byte) 35h Reserved 55h Extended CMOS 75h Extended CMOS
16h Base memory (low byte) 36h Reserved 56h Extended CMOS 76h Extended CMOS
17h Extended Memory (high byte) 37h Reserved 57h Extended CMOS 77h Extended CMOS
18h Extended Memory (low byte) 38h Reserved 58h Extended CMOS 78h Extended CMOS
19h 0 (C:) Hard Disk Type 39h Reserved 59h Extended CMOS 79h Extended CMOS
1Ah 1 (D:) Hard Disk Type 3Ah Reserved 5Ah Extended CMOS 7Ah Extended CMOS
1Bh Reserved 3Bh Reserved 5Bh Extended CMOS 7Bh Extended CMOS
1Ch Supervisor Password 3Ch Reserved 5Ch Extended CMOS 7Ch Extended CMOS
1Dh Supervisor Password 3Dh Reserved 5Dh Extended CMOS 7Dh Extended CMOS
1Eh Reserved 3Eh Reserved 5Eh Extended CMOS 7Eh Extended CMOS
1Fh Reserved 3Fh Reserved 5Fh Extended CMOS 7Fh Extended CMOS

在 CMOS RAM中,存放着当前的时间:年、月、日、时、分、秒。这6个信息的长度都为1个字节,存放见上面列表。

这些数据以 BCD 码的方式存放。
BCD码是以4位二进制数表示十进制数码的编方法。


例:数值 26,用BCD码表示为:0010 0110



一个字节可以表示两个BCD码。
CMOS RAM存储时间信息的单元中,存储了用两个BCD码表示的两位十进制数,高4位BCD码表示十位,低4位BCD码表示个位。

例:00010100B表示14。



编程:在屏幕中间显示当前的月份。
              assume    cs:code
        code  segment
      start:  mov       al,8        ; 读 CMOS RAM 8号单元
              out       70h,al
              in        al,71h
              mov       ah,al       ; 分离取出的BCD码为十位和个位
              mov       cl,4
              shr       ah,cl
              and       al,00001111b
              add       ah,30h
              add       al,30h
              mov       bx,0b800h
              mov       es,bx
              mov       byte ptr es:[160*12+40*2],ah
              mov       byte ptr es:[160*12+40*2].2,al
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
编程:以 “年/月/日 时:分:秒” 的格式,显示当前日期、时间。
;---------------------------------------------------
;
;             年/月/日  时:分:秒
;---------------------------------------------------

              assume    cs:code
        data  segment
              db        9,8,7,4,2,0 ; (年月日时分秒)在CMOS中的地址
              db        '/','/',' ',':',':'     ; 日期时间间隔符号
        data  ends
        code  segment
      start:  mov       ax,20h
              mov       ss,ax
              mov       sp,16
              mov       ax,data
              mov       ds,ax
              mov       ax,0b800h
              mov       es,ax
              mov       si,0
              mov       di,0
              xor       ax,ax

              mov       cx,6
          s:  mov       al,[si]     ; 读CMOS信息
              out       70h,al
              in        al,71h
              mov       ah,al       ; 转BCD码
              push      cx
              mov       cl,4
              shr       ah,cl
              pop       cx
              and       al,00001111b
              add       ah,30h
              add       al,30h
              mov       byte ptr es:[160*12+30*2+di],ah
              mov       byte ptr es:[160*12+30*2+di].2,al

              mov       al,ds:[si+6]            ; 显示日期与时间间隔符号
              mov       es:[160*12+30*2+di].4,al

              inc       si
              add       di,6
              loop      s

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
[ Last edited by redtek on 2007-1-11 at 08:36 PM ]
附件 1: 1.GIF (2007-1-12 09:35, 6.59 K, 下载附件所需积分 1点 ,下载次数: 1)



作者: lxmxn     时间: 2007-1-11 09:29

  好久没有来兄这里看了,惭愧……

  好想和兄一起学习汇编,但是由于学业比较繁重,很多课程都来不及搞,所以只有先把汇编放一放了,等有时间再来和兄一起学习汇编,呵呵,恐怕到那时候兄已经是汇编牛人了,^_^

作者: redtek     时间: 2007-1-11 23:35
等兄有时间了我们一起学习更多更好玩的东东~:)
经常看到批处理区兄的精彩代码,水平越来越高,真是为之高兴~:)
希望经常看到兄的创造性思想和好玩的代码~:)))
作者: redtek     时间: 2007-1-11 23:50    标题: 【80x86汇编语言学习笔记】

逻辑移位指令


shl和Shr逻辑移位指令。

shl 左移
shr 右移



SHL逻辑左移指令,功能

1) 将一个寄存器或内存单元中的数据向左移位
2) 将最后移出的一位写入CF中
3) 最低位用0补充



指令:
              assume    cs:code
        code  segment
      start:  mov       al,01001000b
              shl       al,1
              mov       ah,4ch
              int       21h
        code  ends
              end       start
结果:

  Quote:
C:\Masm50>p256.exe

C:\Masm50>echo %errorlevel%
144

返回值144(十进制)它的二进制是:10010000B ,左移了一位~:)



最后移出的一位写入CF中
              assume    cs:code
        code  segment
      start:  mov       al,10010000b
              shl       al,1
              mov       ah,4ch
              int       21h
        code  ends
              end       start
红色标示CF
绿色标示结果

  Quote:
C:\Masm50>debug p256.exe
-r
AX=0000  BX=0000  CX=0008  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 B090          MOV     AL,90
-u
0B49:0000 B090          MOV     AL,90
0B49:0002 D0E0          SHL     AL,1
0B49:0004 B44C          MOV     AH,4C
0B49:0006 CD21          INT     21
0B49:0008 8D867AFE      LEA     AX,[BP+FE7A]
0B49:000C 50            PUSH    AX
0B49:000D E8E270        CALL    70F2
0B49:0010 83C406        ADD     SP,+06
0B49:0013 B8C805        MOV     AX,05C8
0B49:0016 50            PUSH    AX
0B49:0017 8D4680        LEA     AX,[BP-80]
0B49:001A 50            PUSH    AX
0B49:001B E83E0D        CALL    0D5C
0B49:001E 83C404        ADD     SP,+04
-t

AX=0090  BX=0000  CX=0008  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0002   NV UP EI PL NZ NA PO NC
0B49:0002 D0E0          SHL     AL,1
-t

AX=0020  BX=0000  CX=0008  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0004   OV UP EI PL NZ NA PO CY

如果移动位数大于1时,必须将移动位数放在CL中。
              assume    cs:code
        code  segment
      start:  mov       al,01010001b
              mov       cl,3
              shl       al,cl
              mov       ah,4ch
              int       21h
        code  ends
              end       start
左移了3位,值:10001000B



右移,与左移shl操作相反。

1) 将一个寄存器或内存单元中的数据向右移位
2) 将最后移出的一位定入CF中
3) 最高位用0补充


将X逻辑左移一位,相当于执行 X=X*2
将X逻辑右移一位,相当于执行 X=X/2





编程:用加法和移位指令计算(AX)=(AX)*10


加法运算(AX)=(AX)*10
运算 3*10
              assume    cs:code
        code  segment
      start:  mov       ax,0
              mov       cx,10
          s:  add       ax,3
              loop      s
              mov       ah,4ch
              int       21h
        code  ends
              end       start
结果

  Quote:
C:\Masm50>p258.exe

C:\Masm50>echo %errorlevel%
30

移位指令计算(AX)=(AX)*10
计算 3*10

X左移1位就相当于X*2
X左移2位…………  X*4
X左移3位…………  X*8

(AX)*10=(AX)*2+(AX)*8
(AX)*10=(AX)左移1位+(AX)左移3位
              assume    cs:code
        code  segment
      start:  mov       al,3
              mov       cl,3
              shl       al,cl
              add       al,3
              add       al,3
              mov       ah,4ch
              int       21h
        code  ends
              end       start
[ Last edited by redtek on 2007-1-11 at 12:08 PM ]
作者: ccwan     时间: 2007-1-12 06:54
这样的网志堪称经典了,向兄学习。
作者: redtek     时间: 2007-1-12 09:39
有ccwan兄的不断鼓励,我准备把这网志写溢出了,哈哈……

[ Last edited by redtek on 2007-1-11 at 08:44 PM ]
作者: redtek     时间: 2007-1-12 09:44    标题: 【80x86汇编语言学习笔记】

外中断


接口芯片和端口


在PC系统的接口卡和主板上,装有各种接口芯片。
这些外设接口芯片的内部有若干寄存器,CPU将这些寄存器当作端口来访问。


外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中。
CPU向外设输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。

CPU还可以向外设输出控制命令,而这些控制命令也是先送到相关芯片的端口中,然后再由相关的芯片根据命令对外设实施控制。


CPU通过端口和外部设备进行联系。


外中断信息


在PC系统中,外中断源一共有两类:

1) 可屏蔽中断


可屏蔽中断中CPU可以不响就的外中断。
IF=1,则CPU在执行完当前指令后响应中断,引发中断过程。
IF=0,则不响应可屏蔽中断。

IF位于FLAG第9位。


关于中断所引发的中断过程:

1) 取中断类型码N
2) 标志寄存器入栈 IF=0,TF=0
3) CS、IP入栈
4) (IP)=(N*4), (CS)=(N*4+2)

可屏蔽中断所引发的中断过程,除与第1步实现上有所不同外,基本和内中断的中断过程相同。

因为可屏蔽中断信息来自于CPU外部,中断类型是通过数据总线送入CPU的。
而内中断的中断类型码是在CPU内部产生的。

将IF置0的原因:在进入中断处理程序后,禁止其他的可屏蔽中断。


如果在中断处理程序中需要处理可屏蔽中断,可以用指令将IF置1:
8086CPU提供设置IF指令:


  STI  用于设置IF=1 (允许响应中断)
  CLI  用于设置IF=0 (不响应中断)


2) 不可屏蔽中断


不可屏蔽中断是CPU必须响应的外中断。


对于8086CPU,不可屏蔽中断的中断类型码固定为2。
所以中断过程中,不需要取中断类型码。



不可屏蔽中断的中断过程为:

  1) 标志寄存器入栈 IF=0, TF=0;
  2) CS、IP入栈
  3) (IP)=8, (CS)=(0AH)



几乎所有由外设引发的外中断,都是可屏蔽中断。



PC机键盘的处理过程


键盘输入的处理过程:

1) 键盘产生扫描码
2) 扫描码送入60H端口
3) 引发9号中断
4) CPU执行INT9中断例程处理键盘输入

上面第1、2、3步均由硬件完成。



BIOS键盘缓冲区是系统启动后,BIOS用于存放INT9中断例程所接收键盘输入的内存区,可存储15个键盘输入。

INT9中断例程除了接收扫描码外,还要产生和扫描码对应的字符码,所以BIOS键盘缓冲区中,一个键盘输入用一个字单元存放。

高位字节存放扫描码,低位字节存放字符码。


0040:17 单元存储键盘状态字节,该字节记录了控制键和切换键的状态。




显示从 a ~~ z :
              assume    cs:code
        code  segment
      start:  mov       ax,0b800h
              mov       es,ax
              mov       ah,'a'
          s:  mov       es:[160*12+40*2],ah
              inc       ah
              cmp       ah,'z'
              jna       s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
简单延时-循环
              assume    cs:code
        code  segment
      start:  mov       dx,0afffh
              mov       ax,0ffffh
          s:  sub       ax,1
              sbb       dx,0
              cmp       ax,0
              jne       s
              cmp       dx,0
              jne       s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
显示从 a ~~ z 字母,每隔一定时间显示下一个字母
              assume    cs:code
       stack  segment
              db        128 dup (0)
       stack  ends
        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,128
              mov       ax,0b800h
              mov       es,ax
              mov       ah,'a'
          s:  mov       es:[160*12+40*2],ah
              call      delay
              inc       ah
              cmp       ah,'z'
              jna       s
              mov       ax,4c00h
              int       21h


      delay:  push      ax          ; 循环延时
              push      dx
              mov       dx,1000h
              mov       ax,0
         s1:  sub       ax,1
              sbb       dx,0
              cmp       ax,0
              jne       s1
              cmp       dx,0
              jne       s1
              pop       dx
              pop       ax
              ret

        code  ends
              end       start
键盘输入到达60H端口后,就会引发9号中断,CPU则转去执行INT9中断例程。要求编写INT9中断例程,功能如:

1) 从60H端口读出键盘的输入
2) 调用BIOS的INT9中断例程,处理其他硬件细节
3) 判断是否为ESC键扫描码,如果是,改变显示的颜色后返回;
   如果不是则直接返回。


编写自己的INT9中断例程,但原INT9有一些复杂的硬件细节处理,所以在我们自己的INT9中断例程中还要调用原INT9。

所以,在中断向量表内将INT9中断例程入口地址改写为我们自己的中断例程前,需要先保存原INT9。


当保存原INT9中断例程的偏移地址和段地址后,再调用它时就不能使用INT9来调用了。需要用别的指令来对INT指令进行模拟。


在用其它指令对INT指令进行模拟前,需要先看一下INT指令在执行的时候CPU是如何工作的:

1) 取中断类型码 N
2) 标志寄存器入栈
3) IF=0、 TF=0
4) CS、IP入栈
5) (IP)=(N*4), (CS)=(N*4+2)


取中断类型码是为了定位中断例程的入口地址。
但是,当我们将原INT9中断入口地址保存到另一个内存区域时,我们就不需要中断类型码了,因为我们知道那块自己指定的在内存在哪。




假设已将原INT9中断例程的入口地址保存到了 ds:0 与 ds:2 单元中。现在模拟int过程:

1) 标置寄存器入栈
2) IF=0、TF=0
3) CS、IP入栈
4) (IP)=((DS)*16+0) , (CS)=((CS)*16+2)


上面第3步是CS、IP入栈,因为如果不入栈调用完中断或是子程序就回不到调用者了。
不过,如果是引发INT中断例程的话,CPU是会帮我们做保存CS与IP(入栈)的,所以不用我们自己来。

可是,现在不是要调用INT中断例程,因为原INT9已被我们备份到另一个地址(它的入口地址已经不在中断向量表项中了)。所以我们如果需要调用INT9(就是要执行它,然后再返回到调用者处)就必须使用call命令。

而CALL命令有一个特点:执行CALL命令时,IP指向CALL命令下一条指令(为的是将来返回调用者继续执行CALL后面的指令)。如果CALL是段内CALL,则CPU将IP压栈(不用我们压,这个过程是CPU自动完成的);如果CALL是段间(即:CS:IP)方式,则CPU会自动压入CS、IP(都要压入,不然回不来了)。

所以,上面的CALL的过程和INT调用过程在压入CS、IP入栈的过程是一模一样的。




CALL与调用中断例程不一样的地方是:调用中断例程时CPU自动先压标志寄存器,然后……

而CALL执行时CPU压IP或是CS:IP,但它并不压标志寄存器入栈。
所以如果想用CALL来模拟调用中断例程的方式使用存在另一块内存地址的 DS:0 和 DS:2 的原INT9中断例程,则需要我们:

  (1) 自己压入标志寄存器
  (2) 自己设置IF=0,TF=0(这些得自己做,CALL指令不会帮我们做的,只有INT调用CPU才会帮我们做)
  (3) 使用 call dword ptr ds:[0] 的方式(4字)调用原INT9入口地址,执行原INT9例程。




如何将标志寄存器入栈? 

pushf指令可以实现。



如何将标志寄存器的IF与TF的值设置为0?

  IF 标志寄存器的第9位 
  TF 标志寄存器的第8位

关于IF与TF的含义:

  IF 表示可屏蔽中断
  TF 表示单步中断



为什么要禁止单步中断?

当TF=1(允许单步中断)时,CPU在执行完一条指令后将此发单步中断,转去执行中断处理程序。

而中断处理程序也是由一条条指令组成,如果在执行中断处理程序之前,TF=1,则CPU在执行完中断处理程序的第一条指令后又要产生单步中断,则又要转去执行单步中断的中断处理程序,在执行完中断处理程序的第一条指令后,又要产生单步中断,则又要转去执行单步中断的中断处理程序……无穷无尽的下去……

所以,有时候必须将TF=0(设置为禁卡单步中断),这样才能正确的连续的执行需要的过程。


不可屏蔽的中断类型码固定为:2
中断过程中不需要取中断类型码。不可屏蔽中断过程的寻址部分:

  (IP)=(8), (CS)=(0AH)

其实上面的计算过程如下原理:

  (IP)=(2*4)
  (CS)=(2*4+2)

  不可屏蔽中断是在系统中有必须处理的紧急情况发生时用来通知CPU的中断信息。




编程:在屏幕中间依次显示 a~~z ,延时处理显示在屏幕上。
   在显示过程中,按下 Esc 键后,改变显示颜色。

              assume    cs:code
       stack  segment
              db        128 dup (0)
       stack  ends
        data  segment
              dw        0,0
        data  ends
        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,128

              mov       ax,data
              mov       ds,ax

              mov       ax,0
              mov       es,ax

              push      es:[9*4]    ; 备份原int 9中断例程入口地址
              pop       ds:[0]
              push      es:[9*4+2]
              pop       ds:[2]

              mov       word ptr es:[9*4],offset int9
              mov       es:[9*4+2],cs

              mov       ax,0b800h   ; 显示从 'a'~'z' 字母
              mov       es,ax
              mov       ah,'a'
          s:  mov       es:[160*12+40*2],ah
              call      delay       ; 延时
              inc       ah
              cmp       ah,'z'
              jna       s
              mov       ax,0
              mov       es,ax

              push      ds:[0]      ; 恢复原int9中断向量表项中的入口地址
              pop       es:[9*4]
              push      ds:[2]
              pop       es:[9*4+2]

              mov       ax,4c00h
              int       21h


      delay:  push      ax          ; 延时子程序
              push      dx
              mov       dx,1000h
              mov       ax,0
         s1:  sub       ax,1
              sbb       dx,0        ; 再计算高位并减去借位
              cmp       ax,0
              jne       s1
              cmp       dx,0
              jne       s1          ; 当 ax 与 dx 值都为0时才算递减尽
              pop       dx
              pop       ax
              ret


       int9:  push      ax          ; ------------------------------
              push      bx          ; 自定义 int 9 中断例程
              push      es          ; ------------------------------

              in        al,60h

              pushf                 ; 备份标志寄存器的值
              pushf                 ; 压入标志寄存器用来修改IF、TF值
              pop       bx
              and       bh,11111100b
              push      bx
              popf                  ; 弹出修改好的flag值,修改当前标志寄存器值

              call      dword ptr ds:[0]

              cmp       al,1        ; 比对是否按了 Esc 键 (Esc 键扫描码: 01 )
              jne       int9ret

              mov       ax,0b800h
              mov       es,ax
              inc       byte ptr es:[160*12+40*2+1]         ; 改变颜色

    int9ret:  pop       es
              pop       bx
              pop       ax
              iret

        code  ends
              end       start
) 分析上面int9中断例程,精简指令

  进入中断例程后,IF和TF都已经置0。

  
  pushf
  pushf
  pop ax
  and ah,11111100b
  push ax
  popf
  call dword ptr ds:[0]
  
  上面批令可以精简为:

  
  pushf
  call dword ptr ds:[0]
  
精减后的全部代码如下:
              assume    cs:code
       stack  segment
              db        128 dup (0)
       stack  ends
        data  segment
              dw        0,0
        data  ends
        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,128

              mov       ax,data
              mov       ds,ax

              mov       ax,0
              mov       es,ax

              push      es:[9*4]    ; 备份原int 9中断例程入口地址
              pop       ds:[0]
              push      es:[9*4+2]
              pop       ds:[2]

              mov       word ptr es:[9*4],offset int9
              mov       es:[9*4+2],cs

              mov       ax,0b800h   ; 显示从 'a'~'z' 字母
              mov       es,ax
              mov       ah,'a'
          s:  mov       es:[160*12+40*2],ah
              call      delay       ; 延时
              inc       ah
              cmp       ah,'z'
              jna       s
              mov       ax,0
              mov       es,ax

              push      ds:[0]      ; 恢复原int9中断向量表项中的入口地址
              pop       es:[9*4]
              push      ds:[2]
              pop       es:[9*4+2]

              mov       ax,4c00h
              int       21h


      delay:  push      ax          ; 延时子程序
              push      dx
              mov       dx,1000h
              mov       ax,0
         s1:  sub       ax,1
              sbb       dx,0        ; 再计算高位并减去借位
              cmp       ax,0
              jne       s1
              cmp       dx,0
              jne       s1          ; 当 ax 与 dx 值都为0时才算递减尽
              pop       dx
              pop       ax
              ret


       int9:  push      ax          ; ------------------------------
              push      bx          ; 自定义 int 9 中断例程
              push      es          ; ------------------------------

              in        al,60h

              pushf                 ; 备份标志寄存器的值
              call      dword ptr ds:[0]

              cmp       al,1        ; 比对是否按了 Esc 键 (Esc 键扫描码: 01 )
              jne       int9ret

              mov       ax,0b800h
              mov       es,ax
              inc       byte ptr es:[160*12+40*2+1]         ; 改变颜色

    int9ret:  pop       es
              pop       bx
              pop       ax
              iret

        code  ends
              end       start
) 分析上面主程序
  在主程序中,如果执行设置INT9中断例程的段地址和篇移地址的指令之间发生了键盘中断,
  则CPU将转去一个错误的地址并执行,将发生错误。
  
排除潜在错误的完全代码:
              assume    cs:code
       stack  segment
              db        128 dup (0)
       stack  ends
        data  segment
              dw        0,0
        data  ends
        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,128
              mov       ax,data
              mov       ds,ax
              mov       ax,0
              mov       es,ax

              cli                   ; 不响应可屏蔽中断
              push      es:[9*4]    ; 备份原int 9中断例程入口地址
              pop       ds:[0]
              push      es:[9*4+2]
              pop       ds:[2]
              mov       word ptr es:[9*4],offset int9
              mov       es:[9*4+2],cs
              sti                   ; 响应可屏幕中断

              mov       ax,0b800h   ; 显示从 'a'~'z' 字母
              mov       es,ax
              mov       ah,'a'
          s:  mov       es:[160*12+40*2],ah
              call      delay       ; 延时
              inc       ah
              cmp       ah,'z'
              jna       s
              mov       ax,0
              mov       es,ax

              cli
              push      ds:[0]      ; 恢复原int9中断向量表项中的入口地址
              pop       es:[9*4]
              push      ds:[2]
              pop       es:[9*4+2]
              sti
              mov       ax,4c00h
              int       21h


      delay:  push      ax          ; 延时子程序
              push      dx
              mov       dx,1000h
              mov       ax,0
         s1:  sub       ax,1
              sbb       dx,0        ; 再计算高位并减去借位
              cmp       ax,0
              jne       s1
              cmp       dx,0
              jne       s1          ; 当 ax 与 dx 值都为0时才算递减尽
              pop       dx
              pop       ax
              ret


       int9:  push      ax          ; ------------------------------
              push      bx          ; 自定义 int 9 中断例程
              push      es          ; ------------------------------
              in        al,60h
              pushf                 ; 备份标志寄存器的值
              call      dword ptr ds:[0]
              cmp       al,1        ; 比对是否按了 Esc 键 (Esc 键扫描码: 01 )
              jne       int9ret

              mov       ax,0b800h
              mov       es,ax
              inc       byte ptr es:[160*12+40*2+1]         ; 改变颜色

    int9ret:  pop       es
              pop       bx
              pop       ax
              iret

        code  ends
              end       start
 
 
 
  


编程:安装新的 int 9 中断例程,使得原 int 9 中断例程的功能得到扩展。
   在DOS下,按F1键后改变当前屏幕的显示颜色,其他的键照常处理。
              assume    cs:code

       stack  segment
              db        128 dup (0)
       stack  ends

        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,128
              push      cs
              pop       ds
              mov       ax,0
              mov       es,ax
              mov       si,offset int9
              mov       di,204h
              mov       cx,offset int9end - offset int9
              cld
              rep       movsb
              push      es:[9*4]
              pop       es:[200h]
              push      es:[9*4+2]
              pop       es:[202h]
              cli
              mov       word ptr es:[9*4],204h
              mov       word ptr es:[9*4+2],0
              sti
              mov       ax,4c00h
              int       21h



       int9:  push      ax
              push      bx
              push      cx
              push      es
              in        al,60h
              pushf
              call      dword ptr cs:[200h]
              cmp       al,3bh
              jne       int9ret
              mov       ax,0b800h
              mov       es,ax
              mov       bx,1
              mov       cx,2000
          s:  inc       byte ptr es:[bx]
              add       bx,2
              loop      s
    int9ret:  pop       es
              pop       cx
              pop       bx
              pop       ax
              iret
    int9end:  nop

        code  ends
              end       start
在 Windows CMD 下调试先运行安装程序。然后进入一个16位程序界面(DEBUG、EDIT等),然后在里面按F1键有效。

此代码正常运行环境: MS-DOS





编程:安装新的 int 9 中断例程

   在DOS下,按下 “A” 键后,除非不再松开,如果松开,就显示满屏幕 “A” ,其他键照常处理。

   按下一个键时产生的扫描码称为通码
   松开一个键时产生的扫描码称为断码
   断码=通码+80H
              assume    cs:code

       stack  segment
              db        128 dup (0)
       stack  ends

        code  segment
      start:  mov       ax,stack
              mov       ss,ax
              mov       sp,128
              push      cs
              pop       ds
              mov       si,offset int9
              mov       ax,0
              mov       es,ax
              mov       di,204h
              mov       cx,offset int9end - offset int9
              cld
              rep       movsb
              push      es:[9*4]
              pop       es:[200h]
              push      es:[9*4+2]
              pop       es:[202h]
              cli
              mov       word ptr es:[9*4],204h
              mov       word ptr es:[9*4+2],0
              sti
              mov       ax,4c00h
              int       21h

       int9:  push      ax
              push      bx
              push      cx
              push      es
              in        al,60h
              pushf
              call      dword ptr cs:[200h]
              add       al,80h
              cmp       al,1eh
              jne       int9iret
              mov       ax,0b800h
              mov       es,ax
              mov       bx,0
              mov       cx,2000
          s:  mov       byte ptr es:[bx],'A'
              add       bx,2
              loop      s
   int9iret:  pop       es
              pop       cx
              pop       bx
              pop       ax
              iret
    int9end:  nop

        code  ends
              end       start
上面的代码原理从个人理解角度讲,已经可以做好玩的东东了~:)



指令系统总结

1 数据传送指令
2 算术运算指令
3 逻辑指令
4 转移指令
5 处理机控制指令
6 串处理指令

[ Last edited by redtek on 2007-1-13 at 11:06 PM ]
作者: redtek     时间: 2007-1-14 12:07    标题: 【80x86汇编语言学习笔记】

直接定址表


(到106楼的时候,现在中文打字的速度早已经超过100字以上/每分钟了~:)

描述单元长度的标号



例程:将 code 段中的 a 标号处的 8 个数据累加,结果存储到 b 标号处的字中。
              assume    cs:code
        code  segment
          a:  db        1,2,3,4,5,6,7,8
          b:  dw        0
      start:  mov       si,offset a
              mov       bx,offset b
              mov       cx,8
          s:  mov       al,cs:[si]
              mov       ah,0
              add       cs:[bx],ax
              inc       si
              loop      s

              mov       ax,4c00h
              int       21h

        code  ends
              end       start
程序中,code、a、b、start、s 都是标号,它们仅仅表示了内存单元地址。

数据标号:除了表示内存单元的地址,还表示了内存单元的长度,即表示在此标号处的单元,是一个字节单元还是字单元,还是双字单元)。

使用这种包含单元长度的标号,可以使我们以简洁的形式访问内存中的数据。
它标记了存储数据的单元的地址和长度。
它不同于仅仅表示地址的地址标号。
              assume    cs:code
        code  segment
           a  db        1,2,3,4,5,6,7,8
           b  dw        0
      start:  mov       si,0
              mov       cx,8
          s:  mov       al,a[si]
              mov       ah,0
              add       b,ax
              inc       si
              loop      s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start
反汇编上面代码:
0B49:000A BE0000        MOV     SI,0000
0B49:000D B90800        MOV     CX,0008
0B49:0010 2E            CS:
0B49:0011 8A840000      MOV     AL,[SI+0000]
0B49:0015 B400          MOV     AH,00
0B49:0017 2E            CS:
0B49:0018 01060800      ADD     [0008],AX
0B49:001C 46            INC     SI
0B49:001D E2F1          LOOP    0010
0B49:001F B8004C        MOV     AX,4C00
0B49:0022 CD21          INT     21
下面程序将 code 段中 a处的8个数据累加,结果存储到b处的dword中,补全程序。
              assume    cs:code
        code  segment
           a  dw        1,2,3,4,5,6,7,8
           b  dd        0
      start:  mov       si,0
              mov       cx,8
          s:  mov       ax,a[si]
              add       word ptr b[0],ax
              adc       word ptr b[2],0
              add       si,2
              loop      s

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
在其他段中使用数据标号


在其他段中,我们也可以使用数据标号来描述存储数据的单元和长度。

在后面加有 “:” 的地址标号,只能在代码段中使用,不能在其他段中使用。


将 data 段中 a 标号处的 8 个数据累加,结果存储到 b 标号处的字中。
              assume    cs:code , ds:data
        data  segment
           a  db        1,2,3,4,5,6,7,8
           b  dw        0
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax

              mov       si,0
              mov       cx,8
          s:  mov       al,a[si]
              mov       ah,0
              add       b,ax
              inc       si
              loop      s

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
如果想在代码段中,直接用数据标号访问数据,则需要用伪指令 assume 将标号所在的段和一个段寄存器联系起来。

否则编译器在编译的时候,无法确定标号的段地址在哪一个寄存器中。

  Quote:
assume        cs:code , ds:data


        data  segment
           a  db        1,2,3,4,5,6,7,8
           b  dw        0
        data  ends

seg 操作符,功能为取得某一标号的段地址。





将 data 段中 a 处的 8 个数据累加,结果存储到 b 处的字中,补全程序。
              assume    cs:code , ds:data

        data  segment
           a  db        1,2,3,4,5,6,7,8
           b  dw        0
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       si,0
              mov       cx,8
          s:  mov       al,a[si]
              mov       ah,0
              add       b,ax
              inc       si
              loop      s
              mov       ax,4c00h
              int       21h
        code  ends
              end       start


  Quote:
-d ds:0
0B49:0000  01 02 03 04 05 06 07 08-24 00 00 00 00 00 00 00

直接定址表



编写程序,以十六进制的形式在屏幕中间显示给定的 Byte 型数据。
              assume    cs:code

        code  segment
      start:  mov       al,0abh
              call      showbyte
              mov       ax,4c00h
              int       21h


   showbyte:  jmp       short show
       table  db        '0123456789ABCDE'

       show:  push      bx
              push      es
              mov       ah,al
              shr       ah,1
              shr       ah,1
              shr       ah,1
              shr       ah,1
              and       al,00001111b

              mov       bl,ah
              mov       bh,0
              mov       ah,table[bx]

              mov       bx,0b800h
              mov       es,bx
              mov       es:[160*12+40*2],ah

              mov       bl,al
              mov       bh,0
              mov       al,table[bx]

              mov       es:[160*12+40*2+2],al
              pop       es
              pop       bx
              ret


        code  ends
              end       start
利用表,在两个数据集合之间建立一种映射关系,可以用查表的方法根据给出的数据得到其在另一集合的对应数据。

1) 为了算法的清晰和简洁
2) 为了加快运算速度
3) 为了使程序易于扩充




程序入口地址的直接定址表


可以在直接定址表中存储子程序的地址,从而方便实现不同子程序的调用。



实现一个子程序 setscreen,为显示输出提供如下功能:

  1) 清屏
  2) 设置前景色
  3) 设置背景色
  4) 向上滚动一行

入口参数说明:

  1) 用ah寄存器传递功能号:
     0 表示清屏
     1 表示设置前景色
     2 表示设置背景色
     3 表示向上滚动一行

  2) 对2、3号功能,用al传送颜色值。(AL)={0,1,2,3,4,5,6,7}


各种功能的实现:

  1) 清屏: 将显存中当前屏幕中的字符设为空格符
  2) 设置前景色: 设置显存中当前屏幕中处于奇地址的属性字节:0、1、2位
  3) 设置背景色: 设置显存中当前屏幕中处于奇地址的属性字节:4、5、6位
  4) 向上滚动一行: 依次将第n+1行的内容复制到第n行处;最后一行为空。
              assume    cs:code

        code  segment
      start:  mov       ah,0        ; 调 segscreen 子程序的 0 号功能 - 清屏
              call      segscreen

              mov       ah,1        ; 调 segscreen 子程序的 1 号功能 - 设前景
              mov       al,1
              call      segscreen

              mov       ah,2        ; 调 segscreen 子程序的 2 号功能 - 设背景
              mov       al,7
              call      segscreen

              mov       ah,3        ; 调 segscreen 子程序的 3 号功能 - 上滚一行
              mov       al,4
              call      segscreen


              mov       ax,     4c00h
              int       21h


  segscreen:  jmp       short set   ; 所有子功能调用索引表 (入口)
      table:  dw        sub1,       sub2,     sub3,       sub4
              ;----------------------------------------------------
              ;         0清屏    1设前景   2设背景   3上滚一行
              ;----------------------------------------------------
        set:  push      bx
              cmp       ah,3        ; 判断功能号是否大于3
              ja        sret
              mov       bl,ah
              mov       bh,0
              add       bx,bx       ; 根据ah中的功能号计算对应子程序在table表中的偏移地址
              call      word ptr table[bx]      ; 调用对应的功能子程序
       sret:  pop       bx
              ret





       sub1:  push      bx          ; 0号子功能: 清屏
              push      cx
              push      es
              mov       bx,0b800h
              mov       es,bx
              mov       bx,0
              mov       cx,2000
      sub1s:  mov       byte ptr es:[bx],' '
              add       bx,2
              loop      sub1s
              pop       es
              pop       cx
              pop       bx
              ret



       sub2:  push      bx          ; 1号子功能: 设置前景色
              push      cx
              push      es
              mov       bx,0b800h
              mov       es,bx
              mov       bx,1
              mov       cx,2000
      sub2s:  and       byte ptr es:[bx],11111000b
              or        es:[bx],al
              add       bx,2
              loop      sub2s
              pop       es
              pop       cx
              pop       bx



       sub3:  push      bx          ; 2号子功能: 设置背景色
              push      cx
              push      es
              mov       cl,4
              shl       al,cl
              mov       bx,0b800h
              mov       es,bx
              mov       bx,1
              mov       cx,2000
      sub3s:  and       byte ptr es:[bx],10001111b
              or        es:[bx],al
              add       bx,2
              loop      sub3s
              pop       es
              pop       cx
              pop       bx
              ret



       sub4:  push      cx          ; 3号子功能: 屏幕向上滚动一行
              push      si
              push      di
              push      es
              push      ds
              mov       si,0b800h
              mov       es,si
              mov       ds,si
              mov       si,160      ; 从第 n+1 行传送到第 n 行
              mov       di,0
              cld
              mov       cx,24       ; 共传送屏幕 24 行数据
      sub4s:  push      cx
              mov       cx,160
              rep       movsb
              pop       cx
              loop      sub4s
              mov       cx,80
              mov       si,0
     sub4s1:  mov       byte ptr [160*24+si],' '
              add       si,2
              loop      sub4s1
              pop       ds
              pop       es
              pop       di
              pop       si
              pop       cx
              ret

        code  ends
              end       start
编写包含多个功能子程序的中断例程

安装一个新的 int 7ch 中断例程,为显示输出提供如下功能子程序:

  1) 清屏
  2) 设置前景色
  3) 设置背景色
  4) 向上滚动一行

入口参数说明:

  1) 用 ah 寄存器传递功能号:

     0 表示清屏
     1 表示设置前景色
     2 表示设置背景色
     3 表示向上滚动一行

  2) 对于2、3号功能,用 al 传递颜色值,(al)={0,1,2,3,4,5,6,7}
              assume    cs:code

        code  segment

;************************************************************
;                       int7ch 安装代码
;************************************************************

      start:  mov       ax,cs
              mov       ds,ax
              mov       si,offset int7ch
              mov       ax,0
              mov       es,ax
              mov       di,200h

              mov       cx,offset int7ch_end - offset int7ch
              cld
              rep       movsb

              mov       word ptr es:[7ch*4],200h
              mov       word ptr es:[7ch*4+2],0

              mov       ax,4c00h
              int       21h



;************************************************************
;                       int7ch 中断例程
;************************************************************

     int7ch:  jmp       short set

       ;table  dw        sub1,sub2,sub3,sub4     ; table = 202h
       ; 编译器在编译的时候认为 table 是上面(中断安装程序)的 cs 地址,
       ; 而无法定位 table 在 200h 的基础上的偏移,所以暂时先Debug出子程序偏移填入 :(

       table  dw        021dh, 0238h,0256h,0278h
        set:  push      bx
              cmp       ah,3
              ja        sret
              mov       bl,ah
              mov       bh,0
              add       bx,bx
              call      word ptr cs:[bx].202h
       sret:  pop       bx
              iret





       sub1:  push      bx          ; int 7ch 0 号子功能:清屏
              push      cx
              push      es
              mov       bx,0b800h
              mov       es,bx
              mov       bx,0
              mov       cx,2000
      sub1s:  mov       byte ptr      es:[bx],' '
              add       bx,2
              loop      sub1s
              pop       es
              pop       cx
              pop       bx
              ret





       sub2:  push      bx          ; int 7ch 1 号子功能:设置前景色
              push      cx
              push      es
              mov       bx,0b800h
              mov       es,bx
              mov       bx,1
              mov       cx,2000
      sub2s:  and       byte ptr es:[bx],11111000b
              or        es:[bx],al
              add       bx,2
              loop      sub2s
              pop       es
              pop       cx
              pop       bx
              ret





       sub3:  push      bx          ; int 7ch 2 号子功能:设置背景色
              push      cx
              push      es
              mov       cl,4
              shl       al,cl       ; al=xxx0000b
              mov       bx,0b800h
              mov       es,bx
              mov       bx,1
              mov       cx,2000
      sub3s:  and       byte ptr es:[bx],10001111b
              or        es:[bx],al
              add       bx,2
              loop      sub3s
              pop       es
              pop       cx
              pop       bx
              ret





       sub4:  push      cx
              push      ds
              push      es
              push      si
              push      di
              mov       si,0b800h
              mov       ds,si
              mov       es,si
              mov       si,160      ; 0-159 = 第1行, 160 = 第2行第0列
              mov       di,0
              cld
              mov       cx,24       ; 处理 24 行
      sub4s:  push      cx
              mov       cx,160      ; 要复制一行的内容,共160字节
              rep       movsb
              pop       cx
              loop      sub4s
              mov       cx,160      ; 清除最后一行内容,因其已复制到上一行了。
     sub4s1:  mov       byte ptr [160*24+si],' '
              add       si,2
              loop      sub4s1
              pop       di
              pop       si
              pop       es
              pop       ds
              pop       cx
              ret

int7ch_end:  nop

        code  ends
              end       start
遇到一个问题,在上面代码的 int 7ch 子程序的注解处。
暂时解决不了,不知道哪个宏指令可以让编译器认为 table 要从 200h 处为基址计算偏移(实际 table 应为 202h)。
       ;table  dw        sub1,sub2,sub3,sub4     ; table = 202h
       ; 编译器在编译的时候认为 table 是上面(中断安装程序)的 cs 地址,
       ; 而无法定位 table 在 200h 的基础上的偏移,所以暂时先Debug出子程序偏移填入 :(
关于数据标号与地址标号的区别分析

  Quote:
      start:  jmp       short do0
         top  dw        0
       top2:  dw        0
        do0:  mov       bx,top
              mov       ax,offset top2

分析过程

  Quote:
C:\Masm50>debug noname.exe
-r
AX=0000  BX=0000  CX=003D  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0000   NV UP EI PL NZ NA PO NC
0B49:0000 EB04          JMP     0006
-u
0B49:0000 EB04          JMP     0006
0B49:0002 0000          ADD     [BX+SI],AL
0B49:0004 0000          ADD     [BX+SI],AL
0B49:0006 2E            CS:
0B49:0007 8B1E0200      MOV     BX,[0002]
0B49:000B B80400        MOV     AX,0004
0B49:000E B001          MOV     AL,01
0B49:0010 8800          MOV     [BX+SI],AL
0B49:0012 2E            CS:
0B49:0013 FF060200      INC     WORD PTR [0002]
0B49:0017 2E            CS:
0B49:0018 8B1E0200      MOV     BX,[0002]
0B49:001C B002          MOV     AL,02
0B49:001E 8800          MOV     [BX+SI],AL
-t

AX=0000  BX=0000  CX=003D  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=0006   NV UP EI PL NZ NA PO NC
0B49:0006 2E            CS:
0B49:0007 8B1E0200      MOV     BX,[0002]                          CS:0002=0000
-t

AX=0000  BX=0000  CX=003D  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000B   NV UP EI PL NZ NA PO NC
0B49:000B B80400        MOV     AX,0004
-t

AX=0004  BX=0000  CX=003D  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B39  ES=0B39  SS=0B49  CS=0B49  IP=000E   NV UP EI PL NZ NA PO NC

当操作数据标号时,是在操作数据标号所示地址中的内容,它也代表长度。
如果 inc 它,则是在 inc 数据标号所示地址中的内容的值递增。

当操作地址标号时,是在操作它的偏移量。

[ Last edited by redtek on 2007-1-18 at 01:42 PM ]
作者: redtek     时间: 2007-1-15 12:33    标题: 【80x86汇编语言学习笔记】

使用BIOS进行键盘输入和磁盘读写


) 键盘输入引发 9 号中断,BIOS提供了 int 9 中断例程。

) CPU在9号中断发生后,执行 int 9中断例程,从60H端口读出扫描码,并将其转化为相应的ASCII码或状态信息。
  它们存储在指定空间(键盘缓冲区或状态字节)中。

) 在CPU执行完INT9中断例程后,都放到了键盘缓冲区中。
  键盘缓冲区中有16个字单元,可以存储15个按键的扫描码和对应的ASCII码。

) 键盘缓冲区是用环形队列结构管理的内存区。




BIOS的INT9中断例程和INT16H中断例程是一对相互配合的程序。

INT9中断例程向键盘缓冲区中写入。
INT16H中断例程从键盘缓冲区中读了。




例程:接收用户的键盘输入,输入r,将屏幕上的字符设置为红色。
   输入g,将屏幕上的字符设置为绿色。输入b,设置为蓝色。
              assume    cs:code
        code  segment
      start:  mov       ah,0
              int       16h
              mov       ah,1
              cmp       al,'r'
              je        red
              cmp       al,'g'
              je        green
              cmp       al,'b'
              je        blue
              jmp       short sret

        red:  shl       ah,1        ; 技巧: 001 左移1后位再被green左移1位,值为100(红色)
      green:  shl       ah,1        ; 如果选择 g ,则只左移一位: 001 左移1位为: 010(绿色)
       blue:  mov       bx,0b800h   ; 因 ah默认前面设置为1 ,所以选择b时直跳这里,001本身就是(蓝色)
              mov       es,bx
              mov       bx,1
              mov       cx,2000
          s:  and       byte ptr es:[bx],11111000b
              or        es:[bx],ah
              add       bx,2
              loop      s

       sret:  mov       ax,4c00h
              int       21h

        code  ends
              end       start
字符串的输入


最基本的字符串输入程序,需具备以下功能:

1) 在输入的同时需要显示这个字符串
2) 一般在输入回车符后,字符串输入结束
3) 能够删除已经输入的字符


编写一个接收字符串的输入子程序,实现上面三个基本功能。

(DH)、(DI)=字符串在屏幕上显示的行、列位置。
DS:SI 指向字符串存储的空间,字符串以0为结尾符。

字符串的存储空间是一个字符栈,字符栈中的所有字符,从栈底到栈顶,组成一个字符串。



程序处理过程:

1) 调用 int 16h 读取键盘输入
2) 如果是字符,进入字符栈,显示字符栈中所有字符;继续执行1
3) 如果是退格键,从字符栈中弹出一个字符,显示字符栈中所有字符;继续执行1
4) 如果是Enter键,向字符栈中压入0,返回。



子程序:字符栈的入栈、出栈和显示

参数说明:(AH)=功能号: 0表示入栈,1表示出栈,2表示显示
     DS:SI   指向字符栈空间
     对于0号功能: (AL)=入栈字符
     对于1号功能: (AL)=返回的字符
     对于2号功能: (DH)、(DL)=字符串在屏幕上显示的行、列位置
              assume    cs:code
        data  segment
              db        256 dup (0)
        data  ends

        code  segment
      start:  mov       ax,data
              mov       ds,ax
              call      getstr      ; 调用字符串接收子程序
              mov       ax,4c00h
              int       21h





     getstr:  push      ax          ; 接收字符串输入子程序
              push      ds
              push      es

    getstrs:  mov       ah,0
              int       16h
              cmp       al,20h
              jb        nochar      ; ASCII码小于0,说明不是字符
              mov       ah,0
              call      charstack   ; Call子程序:字符入栈
              mov       ah,2
              call      charstack   ; 显示栈中的字符
              jmp       getstrs     ; 继续等待从键盘输入的字符
     nochar:  cmp       ah,0eh      ; 退格键扫描码
              je        backspace
              cmp       ah,1ch      ; 回车键扫描码
              je        enter
              jmp       getstrs     ; 继续等待接收字符
  backspace:  mov       ah,1
              call      charstack   ; 字符出栈(删掉了一个字符)
              mov       ah,2
              call      charstack   ; 显示栈中的字符
              jmp       getstrs
      enter:  mov       al,0
              mov       ah,0
              call      charstack   ; 0入栈
              ; mov       ah,2
              ; call      charstack   ; 显示栈中的字符

              pop       es
              pop       ds
              pop       ax
              ret



  charstack:  jmp       short charstart
       table  dw        charpush,charpop,charshow
         top  dw        0
  charstart:  push      bx
              push      dx
              push      di
              push      es
              cmp       ah,2
              ja        sret
              mov       bl,ah
              mov       bh,0
              add       bx,bx
              jmp       word ptr table[bx]

   charpush:  mov       bx,top      ; 0号功能: 字符入栈
              mov       [si][bx],al
              inc       top
              jmp       sret

    charpop:  cmp       top,0       ; 1号功能: 字符出栈
              je        sret        ; 如果已是栈顶,则返回
              dec       top         ; 否则栈顶减1表示已出栈
              mov       bx,top
              mov       al,[si][bx]
              jmp       sret

   charshow:  mov       bx,0b800h   ; 2号功能: 显示字符
              mov       es,bx
              mov       al,160
              mov       ah,0
              mul       dh          ; dh=行(dh*160), dl=列
              mov       di,ax
              add       dl,dl       ; n列总字符数
              mov       dh,0
              add       di,dx
              mov       bx,0        ; 清零
  charshows:  cmp       bx,top
              jne       noempty
              mov       byte ptr es:[di],' '
              jmp       sret
    noempty:  mov       al,[si][bx]
              mov       es:[di],al
              mov       byte ptr es:[di+2],' '
              inc       bx
              add       di,2
              jmp       charshows

       sret:  pop       es
              pop       di
              pop       dx
              pop       bx
              ret

        code  ends
              end       start
[ Last edited by redtek on 2007-1-16 at 09:55 PM ]
作者: redtek     时间: 2007-1-17 01:32    标题: 【80x86汇编语言学习笔记】

看 本是 大师的贴子跟着学~:)


34字节DOS EXE文件,最短了?
http://www.cn-dos.net/forum/viewthread.php?tid=26748&fpage=1


操作步骤

  Quote:
C:\Masm50\temp>DEBUG
-N P
-L
-R
AX=0000  BX=0000  CX=0204  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF5  ES=0AF5  SS=0AF5  CS=0AF5  IP=0100   NV UP EI PL NZ NA PO NC
0AF5:0100 4D            DEC     BP
-RIP
IP 0100
:0
-D
0AF5:0100  4D 5A 04 00 02 00 00 00-20 00 00 00 FF FF 00 00   MZ...... .......
0AF5:0110  00 00 ED 36 00 00 00 00-1E 00 00 00 01 00 00 00   ...6............
0AF5:0120  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0170  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-E 0120 B4 4C CD 21
-E 0104 01
-E 0108 02
-D CS:100
0AF5:0100  4D 5A 04 00 01 00 00 00-02 00 00 00 FF FF 00 00   MZ..............
0AF5:0110  00 00 ED 36 00 00 00 00-1E 00 00 00 01 00 00 00   ...6............
0AF5:0120  B4 4C CD 21 00 00 00 00-00 00 00 00 00 00 00 00   .L.!............
0AF5:0130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0AF5:0170  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-RCX
CX 0204
:24
-W
Writing 00024 bytes
-Q

修改后的保留文件头为 32 字节,加上所写的指令为4字节(mov ah,4ch 和 int 21h),一共占了32+4=36字节~:)


Debug移动代码到某个指定的内存区的命令给忘了,哈哈……

关于修改 04h 与 08h 文件头的内容还不清楚,现在去找 .exe 文件头的资料~:)))


(在本是大师贴子的第4楼关于文件结构的内容)
http://www.cn-dos.net/forum/redirect.php?fid=6&goto=lastpost



相关资料:文件格式


(以下节选出自 http://db.kingsoft.com/lianzai/3.2.shtml - 《数量最多的病毒--文件型病毒》一文 

  Quote:
  可执行文件格式:

  在DOS环境下有四种基本的可执行文件格式:

  批处理文件,是以.BAT结尾的文件,在BAT文件中可以包括一些DOS命令,以及在批处理文件中调用其它的可执行文件;批文件还有一些简单的流程控制功能,可以实现循环、条件判断等简单的编程工作。

  设备驱动文件,是以.SYS结尾的文件,比如说CONFIG.SYS和IO.SYS等,是DOS操作系统使用的设备驱动程序。

  COM文件,是以.COM结尾的纯代码文件。没有文件头部分,缺省的总是从16进制的100H处开始执行,没有重定位项,这也限制了它的所有代码和数据必须控制在64K以内。

  EXE文件,是以.EXE结尾的文件,这种文件以英文字母“MZ”开头,通常我们称之为MZ文件。MZ文件有一个文件头,用来指出每个段的定义,以及重定位表。.EXE文件摆脱了代码大小最多不能超过64K的限制,是DOS下最主要的文件格式。

  在视窗3.0和视窗3.1版本中,微软推出了一种新的可执行文件格式,在MZ文件头之后又有一个以NE开始的文件头,我们称之为NE文件。由于视窗的可执行文件同DOS相比增加了很多内容,如资源、动态库...。NE格式表现极为复杂,NE格式文件装载程序读取磁盘上的文件后,需要在内存中组装成一个完全不同的数据结构然后开始运行。

  在视窗32位平台(版本9x和版本NT/2000系列),微软又推出了一种新的可执行文件格式,可移植的可执行文件(Portable Executable File)格式。它同NE格式不同的是在MZ文件头之后是一个以“PE”开始的文件头。PE文件格式是从COFF(一个在Unix世界中广泛使用的通用二进制文件格式)的对象格式发展而来的,它同NE格式相比是进了一大步,其文件在磁盘中的格式同内存中的格式区别不大,装载程序实现起来相当简单。

  在视窗32位环境下,微软还有一种应用比较少的可执行文件格式:线性可执行文件(Linear Executable),主要用于设备驱动程序VXD,这种格式是微软和IBM共同开发的,也是IBM的OS/2操作系统使用的可执行文件格式。

(一知半解)

[ Last edited by redtek on 2007-1-20 at 10:24 PM ]
作者: namejm     时间: 2007-1-17 09:25


  Quote:
Originally posted by redtek at 2007-1-16 12:32:
修改后的保留文件头为 32 字节,加上所写的指令为4字节(mov ah,4ch 和 int 21h),一共占了32+4=26字节~:)

  呵呵,学汇编学昏了吧?
作者: redtek     时间: 2007-1-17 09:44
多谢namejm兄提醒,“32+4=26字节”错误更正了~:)
这错误看上去真可乐,哈哈哈………

[ Last edited by redtek on 2007-1-16 at 08:48 PM ]
作者: redtek     时间: 2007-1-18 03:34    标题: 【80x86汇编语言学习笔记】

磁盘操作


常用的 3.5 寸软盘分为上下两面,每面有80个磁道,每个磁道又分为18个扇区,每个扇区的大小为512B。

2面*80磁道*18扇区*512B=1474560

磁盘的实际访问由磁盘控制器进行,通过控置磁盘控制器来访问磁盘。只能以扇区为单位对磁盘进行读写。

在读写扇区时,要给出面号、磁道号和扇区号。
面号和磁道号从0开始,扇区号从1开始。
BIOS提供了对扇区进行读写的中断例程 int 13H。


读取0面0道1扇区的内容到 0:200
              assume    cs:code
        code  segment
      start:  mov       ax,0        ; 起始时寄存器会被自动初始化,也可以不写这句
              mov       es,ax
              mov       bx,200h     ; es:bx 指向接收从扇区读入数据的内存区

              mov       al,1        ; 读取扇区数
              mov       ch,0        ; 磁道号
              mov       cl,1        ; 扇区号
              mov       dh,0        ; 磁头号
              mov       dl,0        ; 驱动器号
              mov       ah,2
              int       13h

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
将 0:200 中的内容写入0面0道1扇区
              assume    cs:code
        code  segment
      start:  mov       ax,0
              mov       es,ax
              mov       bx,200h

              mov       al,1
              mov       ch,0
              mov       cl,1
              mov       dh,0
              mov       dl,0
              mov       ah,03
              int       13h

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
将当前屏幕的内容保存在磁盘
              assume    cs:code
        code  segment
      start:  mov       ax,0b800h
              mov       es,ax
              mov       bx,0

              mov       al,8
              mov       ch,0
              mov       cl,1
              mov       dh,0
              mov       dl,0
              mov       ah,3
              int       13h

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
1.44 - 2880个扇区,从0编号:逻辑扇区为 0~~2879
0面79道18扇区,计算逻辑扇区号:(0*80+79)*18+18-1

[ Last edited by redtek on 2007-1-18 at 12:40 PM ]
作者: redtek     时间: 2007-1-19 06:58    标题: 【80x86汇编语言学习笔记】

中断例程 INT 21H 3F号子功能调用来操作DOS 管道。
通过编程的方式读取一个程序通过管道“发送”过来的内容。

参考资料: http://support.microsoft.com/kb/46376/zh-cn 来自微软网站。

上面链接中所参考的示例代码为: QuickBasic 程序(调用汇编代码完成读取管道内容)
中断 21 Hex, 以函数 3F, 需要注册参数来传递:
AH 编号 3F Hex, = 函数。
BX Handle = 到文件或设备。
CX =: 为要读取字节数。
DS = 非缓冲区区段。 缓冲区将是一个字符串。
DX = 非缓冲区区偏移。
如果 CF=0 , AX=实际读取的字节数
   CF=1 , AX=出错代码




连蒙带猜的Debug试验过程……

  Quote:
-u
0B39:0100 B80000        MOV     AX,0000
0B39:0103 8ED8          MOV     DS,AX
0B39:0105 BA0002        MOV     DX,0200
0B39:0108 B95000        MOV     CX,0050
0B39:010B 31C0          XOR     AX,AX
0B39:010D B43F          MOV     AH,3F
0B39:010F BB0000        MOV     BX,0000
0B39:0112 CD21          INT     21
0B39:0114 90            NOP

(上面 BX 寄存器内应存放文件句柄,按照上面资料的代码注释上说:)
(inregs.bx = 0       ' The is the handle to the CON device is 0  )


-g 114
My is China.

(在这里居然让我输入东东,哈哈……)
(看来我在这里输入的内容会被接收程序当做从管道过来的内容而接收掉)
(它被接收到了 DS:DX 文件数据缓冲区这里所定义的地址)
(那个 “My is China.” 是我输入的内容)



AX=000E  BX=0000  CX=0050  DX=0200  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=0000  ES=0B39  SS=0B39  CS=0B39  IP=0114   NV UP EI PL ZR NA PE NC
0B39:0114 90            NOP


(输入的内容被读到了这里)

-d ds:200
0000:0200  4D 79 20 69 73 20 43 68-69 6E 61 2E 0D 0A 00 00   My is China.....
0000:0210  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0230  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0240  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0250  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0260  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0270  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

通过管道实验过程:
C:\TEMP\str>debug
-d 0:200 22F
0000:0200  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0210  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0000:0220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-q




C:\TEMP\str>copy con demo.txt
My name is Redtek.
Phone: 12345678
hahahaha...
^Z
已已复复制制         1 个个文文件件。。



C:\TEMP\str>type demo.txt | rCON.com



C:\TEMP\str>debug
-d 0:200 22F
0000:0200  4D 79 20 6E 61 6D 65 20-69 73 20 52 65 64 74 65   My name is Redte
0000:0210  6B 2E 0D 0A 50 68 6F 6E-65 3A 20 31 32 33 34 35   k...Phone: 12345
0000:0220  36 37 38 0D 0A 68 61 68-61 68 61 68 61 2E 2E 2E   678..hahahaha...
-q
在内存 0:200H 处,它们全被存到了自定义的( DS:DX )文件数据缓冲区内。
上面的实验过程是调用了 int 21 中断例程的 3FH 子功能来读取通过管道送过来的字符串。支持多行字符串,以 0DH 0AH 为回车换行。


也就是说,这样的原理可以应用在批处理中:
比如将输出的内容暂存在指定的内存中,而不是磁盘上(代替批处理所生成的临时文件)。
当需要的时候再取回来或是通过某种内存操作来分割(如我就想要第3行字符串,其它的都不要)等操作。


(一知半解)

[ Last edited by redtek on 2007-1-20 at 10:23 PM ]
作者: redtek     时间: 2007-1-21 11:27    标题: 【80x86汇编语言学习笔记】

字符串操作



方便的 BIOS int 10 -- 13号子功能调用

在第10行(行从0起,实为第11行),第0列(实为第1列)显示字符串:
              assume    cs:code

        code  segment
      start:  jmp       short do
        msg:  db        "my name is redtek!"
       line:  nop
         do:  mov       ax,seg msg
              mov       es,ax
              mov       bp,offset msg
              mov       al,1        ; 光标跟随移动
              mov       dh,10       ; 10 行
              mov       dl,0        ; 0 列
              mov       bh,0        ; 页号
              mov       bl,2        ; 颜色
              mov       cx,offset line - offset msg
              mov       ah,13h
              int       10h

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
上面显示字符串使用了 BIOS 功能调用。



自动从 DS:SI 装入字符到 AL 寄存器:

LODSB  指令,将 DS:SI 地址中的 byte 字符装入 AL 寄存器,然后 SI+1,每次执行 LODSB 指令时均会如此,方便了字符串的传送。

  Quote:
C:\Masm50>debug
-e 0:200 "My name is Redtek!"
-a
0AF6:0100 mov ds,ax
0AF6:0102 mov si,200
0AF6:0105 lodsb
0AF6:0106 lodsb
0AF6:0107 lodsb
0AF6:0108
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0AF6  ES=0AF6  SS=0AF6  CS=0AF6  IP=0100   NV UP EI PL NZ NA PO NC
0AF6:0100 8ED8          MOV     DS,AX
-t

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0000  ES=0AF6  SS=0AF6  CS=0AF6  IP=0102   NV UP EI PL NZ NA PO NC
0AF6:0102 BE0002        MOV     SI,0200
-t

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0200  DI=0000
DS=0000  ES=0AF6  SS=0AF6  CS=0AF6  IP=0105   NV UP EI PL NZ NA PO NC
0AF6:0105 AC            LODSB
-t

AX=004D  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0201  DI=0000
DS=0000  ES=0AF6  SS=0AF6  CS=0AF6  IP=0106   NV UP EI PL NZ NA PO NC
0AF6:0106 AC            LODSB
-t

AX=0079  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0202  DI=0000
DS=0000  ES=0AF6  SS=0AF6  CS=0AF6  IP=0107   NV UP EI PL NZ NA PO NC
0AF6:0107 AC            LODSB
-t

AX=0020  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0203  DI=0000
DS=0000  ES=0AF6  SS=0AF6  CS=0AF6  IP=0108   NV UP EI PL NZ NA PO NC
0AF6:0108 29C0          SUB     AX,AX
-q

获得命令行输入的参数(类似于批处理中的: %1   %2   %3 ....)

命令行输入的参数位于: DS:0081H 处,以 0AH 结束。
              assume    cs:code

        code  segment
      start:  mov       si,0081h    ; 定位到命令行参数  (psp处偏移量 81h)
          s:  lodsb
              cmp       al,0dh
              je        short view  ; 如果命令行参数结束,则显示它
              jmp       short s

       view:  mov       byte      ptr [si],24h
              mov       ah,9
              mov       dx,0081h
              int       21h

              mov       ax,4c00h
              int       21h
        code  ends
              end       start
[ Last edited by redtek on 2007-1-21 at 01:39 PM ]
作者: redtek     时间: 2007-1-21 11:33    标题: 【Windows环境下32位汇编语言学习笔记】

API原型中前缀详细解释:
  a       Array                                 数组
  b       BOOL (int)                            布尔(整数)
  by      Unsigned Char (Byte)                  无符号字符(字节)
  c       Char                                  字符(字节)
  cb      Count of bytes                        字节数
  cr      Color reference value                 颜色(参考)值
  cx      Count of x (Short)                    x的集合(短整数)
  dw      DWORD   (unsigned long)               双字(无符号长整数)
  f       Flags   (usually multiple bit values) 标志(一般是有多位的数值)
  fn      Function                              函数
  g_      global                                全局的
  h       Handle                                句柄
  i       Integer                               整数
  l       Long                                  长整数
  lp      Long pointer                          长指针
  m_      Data member of a class                一个类的数据成员
  n       Short int                             短整数
  p       Pointer                               指针
  s       String                                字符串
  sz      Zero terminated String                以0结尾的字符串
  tm      Text metric                           文本规则
  u       Unsigned int                          无符号整数
  ul      Unsigned long (ULONG)                 无符号长整数
  w       WORD (unsigned short)                 无符号短整数
  x,y     x, y coordinates (short)              坐标值/短整数
  v       void                                  空
API的参考手册中的函数原型为匈牙利表示法。

[ Last edited by redtek on 2007-1-20 at 10:35 PM ]
作者: redtek     时间: 2007-1-24 06:26    标题: 【MS-DOS操作系统学习笔记】

引导扇区


当计算机从软盘启动,BIOS把引导盘中第1扇区载入地址为 0000:7c00 处的内存中。然后BIOS跑到 0X7C00 处开始执行引导扇区中的代码。

引导扇区的代码完成将整个操作系统载入到计算机内存并开始操作系统的引导。

第1扇区,就是 DOS Boot Record(DOS引导扇区)。


(待续……)

[ Last edited by redtek on 2007-1-25 at 08:14 PM ]
作者: redtek     时间: 2007-1-25 02:57    标题: 彩色仿DOS窗口




  这个彩色仿DOS窗口是跟lxmxn兄学的~:)))

  而且用这种颜色、背景表示DOS命令即酷又生动,更接近真实~~~



作者: redtek     时间: 2007-1-27 05:03    标题: 【MS-DOS操作系统学习笔记】

文件操作


将字符串写入文件内。
              assume    cs:code , ds:data

        data  segment
        file  db        'c:\temp\1.txt',0       ; 定义被创建的文件名称
         buf  db        'This is a test!'       ; 写入文件的内容
     err_msg  db        0ah,'Error!','$'        ; 出错时的提示信息
      ok_msg  db        0ah,'Ok!','$'           ; 操作成功后的提示
      handle  dw        ?
        data  ends


        code  segment
      start:  mov       ax,data
              mov       ds,ax
              mov       dx,offset file

              mov       cx,0
              mov       ah,3ch
              int       21h         ; 创建文件
              jc        error       ; 如果创建出错,转error处
              mov       handle,ax   ; 保存文件句柄

              mov       bx,ax       ; 写文件时文件句柄必须在bx中
              mov       cx,16       ; 写文件长度
              mov       dx,offset buf           ; 要写入文件的内容首地址
              mov       ah,40h
              int       21h
              jc        error       ; 如果写文件出错,则转error处

              mov       bx,handle
              mov       ah,3eh
              int       21h         ; 关闭文件
              jc        error       ; 如果关闭文件出错,转error
              mov       dx,offset ok_msg
              mov       ah,9
              int       21h         ; 操作成功显示
              jmp       end1

      error:  mov       dx,offset err_msg
              mov       ah,9
              int       21h

       end1:  mov       ah,4ch
              int       21h
        code  ends
              end       start
int21 DOS功能调用: 40H  写文件
               3FH  读文件
               3EH  关闭文件
               41H  删除文件
                    ……

[ Last edited by redtek on 2007-1-26 at 04:08 PM ]
作者: redtek     时间: 2007-1-30 02:44    标题: 【Windows32位汇编语言学习笔记】

建立编程环境


Win32可执行文件叫做PE文件。

PE文件的基本结构和DOS可执行文件有很大不同。它把程序中的不同部分分成各种节区(Section),其中可以有一个节区是放置各种资源,如:菜单、对话框、位图、光标、图标和声音等。


可供选择的不同编译器: MASM,TASM,NASM

根据以上编译的优缺点最后选择不同工具软件集合方式开发 Win32 汇编:

  汇编编译器: MASM软件包中的 ML.EXE 做为 Win32 汇编编译器
  资源编译器: 使用 Microsoft Visual Studio 中的 RC.EXE 和 LINK.EXE……

  ……

  目前已被 Steve Hutchesson 缩合创建的开发软件包名为 MASM32,内收集了合适的工具软件、收集导入库、整理出完整的头文件、收集了来自全世界各地Win32汇编爱好者发布的源程序、帮助文档、IDE开发环境……



  MASM32软件包使汇编不再只用来编写简单的程序和少量的核心模块,它的目标完全是为了用汇编写出专业的大型程序。



  MASM32软件包的官方站点(在此下载):
  http://www.movsd.com
 
  支持OS版本:Win2000 and later OS versions
         Win9x OS versions


  开发工作环境的建立:

    · Editplus
    · UltraEdit32
    MSDN
    · Hexedit
    Resource Wor5kshop
    Soft-ICE
    反汇编软件W32DASM
    · Visual C++
  

  破解工具介绍: http://www.e-works.net.cn/Articles/511/Article31815.htm




编译一个例子:
C:\Source\test\test>dir
驱动器 C 中的卷是 DISK-C
卷的序列号是 4089-CA39

C:\Source\test\test 的目录

2007-01-29  20:59    <DIR>          .
2007-01-29  20:59    <DIR>          ..
1998-02-15  21:49               766 Main.ico
2005-07-24  22:25             1,461 Test.asm
2002-01-29  22:01                20 Test.rc
               3 个文件          2,247 字节
               2 个目录  3,169,656,832 可用字节

C:\Source\test\test>ml /c /coff Test.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

Assembling: Test.asm

C:\Source\test\test>rc Test.rc

C:\Source\test\test>link /subsystem:windows Test.obj test.res
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


C:\Source\test\test>dir test.exe
驱动器 C 中的卷是 DISK-C
卷的序列号是 4089-CA39

C:\Source\test\test 的目录

2007-01-29  20:59             3,072 Test.exe
基本开发环境基本安装完成~:)

[ Last edited by redtek on 2007-1-29 at 08:59 PM ]
作者: zhengzhichu01     时间: 2007-2-9 02:25
很有感触啊?真是牛啊
作者: redtek     时间: 2007-2-9 11:24
有好几次都想把书给撕了,要不就想砸点什么东东,……终于把入门的内容给看完了~:)
作者: lizaoyou     时间: 2007-2-18 00:52
浏览后,已找不到合适语言来表达感受。
原来,大师是这样炼成的!
作者: gne3     时间: 2007-2-18 02:33
网志还真多,看的眼都花了
作者: lxmxn     时间: 2007-3-16 01:30

  好久没有来兄的网志看看了,感触良多,看见兄进步的脚印了。
  
  今年我们开了汇编程序设计的课程了,来的机会可能就很多了,哈哈。感谢兄写的这么多好资料。

作者: Billunique     时间: 2007-4-6 02:42
虽然我现在看不懂你的汇编代码,但你学习的脚印和你的心得感悟,读后有如醍醐灌顶~:)
作者: lxmxn     时间: 2007-4-21 14:18
今天用这个将兄的日志全部放我硬盘里面收藏了。再次感谢一下 Redtek 兄。
for,/l,%_,in,(1,1,13)do,wget -q -O G:\lxmxnxm\Studies\Masm\RedTek\masm_%_.html "http://www.cn-dos.net/forum/viewthread.php?tid=25504&fpage=%_"

作者: ccwan     时间: 2007-5-17 08:58
redtek兄是多日不见了,不知什么时候可以再来论坛,大家谈天说地,讨论问题。
作者: online365     时间: 2007-10-30 03:47
谢谢了,我才学汇编..一定会把楼主的心血给盗完的..呵呵..
作者: shanliang8008     时间: 2007-10-31 22:00
最低级的汇编才牛 可惜太难了
作者: shanliang8008     时间: 2007-10-31 22:00
最低级的汇编才牛 可惜太难了
作者: dy2003310     时间: 2008-2-9 14:06
断断续续的看了两个多月
有了点感觉
作者: a574616737     时间: 2008-8-25 10:02
浏览了一遍,好难啊