- 浏览: 2060556 次
- 性别:
- 来自: 厦门
文章分类
- 全部博客 (1409)
- asp/asp.net学习 (241)
- oracle (10)
- delphi (295)
- java (27)
- pb (1)
- 每日点滴 (49)
- 学习方法 (40)
- 思想方面 (104)
- C语言 (5)
- C++ (1)
- 代码重构经验 (5)
- 软件工程 (3)
- 数据库 (99)
- 英语学习 (3)
- mysql (1)
- 该关注的网站或者网页 (42)
- 总结 (7)
- 要去做的事情 (33)
- 算法 (1)
- 网络方面 (29)
- 随感 (96)
- 操作系统 (36)
- UML (12)
- 常用工具的使用 (55)
- 脚本 (7)
- 汇编 (62)
- 数据结构 (2)
- 财务 (38)
- 语文作文 (16)
- 法律 (1)
- 股票 (88)
最新评论
-
devwang_com:
可以,学习了~~
列出文件夹下所有文件夹的树形结构--Dos命令 tree的使用 -
hvang1988:
不管用啊 frxrprt1.PreviewForm.Pare ...
fastReport预览时嵌入到别的窗体 -
00915132:
我也有这个疑问,非常 感 谢
left join加上where条件的困惑 --SQL优化 -
zhuyoulong:
学习了,高效读书
软件架构师要读的书 -
nTalgar:
非常感谢分享!
Application.ProcessMessages用法:
转自:http://www.x86asm.com/DisplayArticle.asp?BoardID=5&ArticleID=13
四 基本的驻留程序
4.1 一个基本的COM程序
DOS之下有两种形式的可执行文件,这两种文件分别是COM文件和EXE文件.其中,COM文件可以迅速地加载和执行,但是其大小不能超过64K字节,只能有一个段,代码段.而且起始地址为100H指令必须为程序的启动指令.EXE文件可以加载到许多个段中,因此程序的大小没有限制,但是程序加载的过程就比较慢,而且对于内存驻留程序来说还会造成更大的麻烦.
以下是一个可以正确执行的COM文件,但其内容是空的;只是一个COM文件的框架,可以把你写的任何应用部分加在这个文件中,形成一个COM格式的内存驻留程序:
;Section 1
cseg segment
assume cs:cseg,ds:cseg
org 100h
;Section 2
start:
ret
;Section 3
cseg ends
end start
上面的程序可以分成三部分,第一部分定义了代码段和数据段分别放在程序中的位置,以及执行代码的起始地址.第二部分是可执行的程序,在这个例子只一个RET指令而已.第三部分是程序包段的终结,其中END叙述包含了程序开始执行地址.
若是把上面的程序经过汇编连接,你会发现所产生的COM文件只有一个字节长.这是因为所产生的COM文件没有程序段前缀(Programsegmetn profix),因为在DOS下所有和COM文件都有相同的程序段前缀.当DOS加载一个COM文件到内存中时,就会自动地产生一份正确的程序段前缀.一个程序在执行的过程中,可以根据需要修改其程序段前缀,但是在一开始,所有COM文件的程序前缀都是相同的.下面是程序前缀的格式.
偏移位置 含义
0000H 程序终止处理子程序地址(INT 20H)
0002H 分配段的结束地址,段值
0004H 保留
0005H 调用DOS的服务
000AH 前一个父程序的IP和CS
000EH 前一个父程序的CONTROL_C处理子程序地址
0012H 前一个父程序包的硬件错误处理子程序地址
0016H 保留
002CH 环境段的地址值
005EH 保留
005CH FCB1
006CH ` FCB2
0080H 命令行的参数和磁盘转移区域
4.2 一个最小的内存驻留程序
上面的程序只是一个一般的DOS程序而已.并不是内存驻留的.以下是一个基本的内存驻留程序结构:
;Section 1
cseg segment
assume cs:cseg;ds:cseg
org 100h
start: ;Section 2
nop
done: ;Section 3
mov dx,offset done
int 27h
;Section 4
cseg ends
end start
和前一个程序相比,这个程序只是增加了一个DONE部分.这个部分使用了INT 27H这个中断调用,来终止并驻留在内存(Terminate and Stay Resident)中.使用INT 27H这个中断调用时,必须设定好一个指针,让这个指针指向内存中可以使用的部分,事实上,这就相当于设置一个COM文件可加载的位置.另外DOS还提供了INT 21H,AH=31H(驻留程序,Keep process),但是使用这个中断调用时,我们必须设定所保留的内存大小,而不是设定一个指针;另外这个中断调用会送出退出码.
使用INT 27H时,必须设定一个指针指向可用存储位置的开头,以便让DOS用来加载稍后执行的程序.DOS本身有一个指针,这个指针是加载COM文件或EXE文件时的基准地址值.INT尿27H 会改变这个指针或为新的数值.同时造成新指针和旧指针之间的存储空间无法让DOS使用因此这样做会造成可用存储位置愈来愈少.
调用INT 27H时所使用的指针是个FAR指针,其中DX存放的是位移指针(Offset pointer),它可以指到64K字节之内的范围.而DOS是段指针(Segment pointer),它可以指到IBM PC中640K字节的任何一个段.在上面的例子中,DS的内容不必另外设定,因为当COM文件加载时,DS的内容就CS的内容相同了.
经常在编写汇编程序时,常犯的一个错误就是:把assume ds:cseg这个叙述误认为是,存放某一预设值到DS中,事实上,汇编语言程序中的Assume叙述不会产生任何的程序代码,这个功能是告诉汇编器做某些必要的假设,以便正确地汇编程序.譬如以下的程序:
cseg segment
.............
assume ds:cseg
mov ah,radix
.............
radix db 16
.............
cseg ends
上面的程序汇编时,当汇编器看到mov ah,radix这个指令时,它就根据assume ds:cseg来产生一定形式的赋值指令.在面的Assume ds:cseg叙述是告诉汇编器,数据段就位于目前的代码段中.这是内存驻留程序的一项重要关键.如果DS的内容和CS不相同时,无论是否有assume 叙述,程序执行时都会失败.
4.3 改良的内存驻留程序
上面所介绍的内存驻留程序实际上没有做任何事,只是驻留在内存中而已.事实上,在START和END之间放入任何程序代码,都只会执行一次而已然后就永远驻留在内存中,除非是使用转移指令转到START的地址去,否则将永远无法被使用.还要注意一点,START的地址值并非固定不变,它会根据程序执行时计算机的状态而改变.
下面的这个程序只是把需要驻留的程序代码装载好,但是并不会执行.
;Section 1
cseg segment
assume cs:cseg,ds:cseg
org 100h
;Section 2
start:
jmp initialize
;Section 3
app_start:
nop
initialize:
;Section 4
mov dx,offset initialize
int 27h
;Section 5
cseg ends
end start
上面的程序一开始执行时就传到initialize标志的地方,装置好驻留在内存的应用部分.原先的DONE已经改成initialize,而驻留在内存的程序代码则放在App_Start 和Initialize之间.
另外,你也许注意到了,程序的起始地址并不是Initialize而是Start.这是因为所有COM程序的起始地址都是100H;而上面的程序中Start是放在100H的地方.如果把Initialize放在End之后,Initialize就变成起始地址,但是这样的程序无法透过EXE2BIN转换成COM文件了.如果无法产生COM文件时,那么就必须直接处理段的内容.
4.4 减少内存的额外负担
到目前为止,都没有接触到程序前缀,当使用INT 27H时,事实上是把指针以前的东西都保留在内存中,这也包括了COM的程序段前缀.因为COM文件执行完毕后,才可以把程序段前缀移掉.
从上面的事实可以看出:如果程序段前缀只能在COM装置程序结束后才可以移去,那么就可以由驻留在内存中的程序代码完成.要做到这一点,可以把整个程序往下移动256个字节.但又如何做到这一点呢?我们可以设定一个标志(Flag),用来指示这个程序是否执行过.如果这个驻留程序或是第一次执行时,就把整个程序往下移动256个字节,以便把程序段前缀移去.但是如果驻留程序在装置好之后,经过一段长时间仍然没有被执行时,怎么办呢?如果同时载入了好几个驻留程序时,双该如何呢?这些重要的事情都需要使用不同的程序代码来解决.如果说这些程序代码超出了256字节时,那么所占用的存储位置就超出程序段前缀所浪费的空间.有些人用一些比较简短的代码来解决这个问题,但是还是比较麻烦.因此对于大部分的内存驻留程序而言,除非存储空间太少,以至于256字节变得很重要,否则最好不要去处理程序段前缀,这样子会让你的程序简洁而且容易阅读.
4.5 使用驻留程序
上面介绍了如何把程序加载到内存,并且让它永远留在内存中,接下来,介绍如何来使用驻留在内存中的程序.
内存驻留程序的使用方法和它原先的设计有密切的关系.譬如,截获键盘输入的程序就必须通过键盘输入的软件中断,或是敲键盘所产生的硬件中断来使用.其它的驻留程序可能就必须靠:系统时钟,系统调用,或是其它的中断才有办法使用.这些驻留程序必须要和以上的使用方法连结;而且在驻留程序安装好之后,至少必须建立一种使用的管道,否则驻留程序将无法使用.
IBM PC必须经由事件来驱动,譬如:键盘,系统时钟,或是软件中断.这些事件可以被截获,然后根据所发生的事件来执行一定的动作.因此必须让中断事件发生时,先执行我们的程序,而非系统的程序.
譬如,当我们设计一个截获键盘输入的驻留程序时,就必须把驻留程序和执行键盘输入的系统调用连结起来.当DOS或是应用程序希望从键盘读取一个字符时,它就必须执行INT 16H调用.因此如果我们能够在调用INT 16H时,先执行我们的驻留程序,那么驻留程序就可能变成应用程序和操作系统间的桥梁.
可以使用INT 21H中断调用中AH=25H来完成以上的要求.设置中断矢量可以更改INT 16H原先的中断矢量内容,让它改为指向我们的程序.譬如以下的例子所示:
cseg segment
assume cs:cseg,ds:cseg
org 100h
start:
jmp Initialize
;Section 1
new_keyboard_io proc far
sti
nop
iret
new_keyboard_io endp
;Section 2
Initialize:
mov dx,offset new_keyboard_io
mov al,16h
mov ah,25h
int 21h
;Section 3
mov dx,offset Initialize
int 27h
cseg ends
end start
上面的程序和4.3的程序结构是一样的,但是仍然有一些重要的改变.在Section 1和Section 2.在Section 1把驻留部分修改成子程序形式(Procedure),这样做是为了增加程序的可读性.另外,驻留部分多加了两个指令,STI和IRET.其中STI是设置中断标志(Set Interrupt Flag)和起始中断(Enable interrupts).
当CPU发生中断时,它就关闭中断标志,因此CPU就不再接受中断.事实上,CPU会专心地为目前发生的中断服务.当CPU停止接受中断时,任何硬件中断的信号都会被忽略,譬如:键盘,时钟脉冲,磁盘机信号,调制解调器的中断.如果CPU一直不接受中断,那么就会漏掉一些重要的信息,计算机系统也可能因此而死机.因此虽然CPU可以停止接受中断一段时间,但是却不能够久.
第二个重要的指令是IRET,从中断返回(Return from interrupt).IRET的功能和RET极相似,RET是用来从被调用 的子程序中返回,而IRET则是用来从中断程序返回.但是使用IRET返回时,它会从堆栈中先取出返回的地址值,然后再取出CPU的状态标志(State Flag).CPU的状态标志在CPU接受中断时,会自动地推入堆栈中.因此执行IRET指令后,CPU的状态就恢复成未中断前的状态;也就是说CPU就可以继续接受外界的中断(CPU状态标志中断包括了中断标志).严格地说,STI和IRET在这个例子中都是多余的,但是对于实际的中断处理程序而言,这两个指令都很重要.
另外,使用设置中断矢量的中断调用时,暂存器AL必须存入所要设置的中断矢量,而中断矢量指针则必须放到暂存器DS:DX中.
4.6 连接中断处理程序
若是把前一节的程序拿来执行时,键盘是无法输入的,事实上,处理键盘的硬件中断处理程序会继续地读取敲入的字符,并且放到等待队列中,直到队列填满为止;但是由于读取等待队列的软件中断INT 16H已经被改变了,因此队列的内容就永远取不出来.
现在写一个中断处理程序,这个中断处理程序只是调用原先的键盘中断处理程序,一旦做到这一点之后,接下来就可以根据键盘的输入做修改.以下就是调用原先键盘处理程序的驻留程序:
cseg segment
assume cs:cseg,ds:cseg
org 100h
start:
jmp Initialize
Old_Keyboard_IO dd ?
;Section 1
new_keyboard_io proc far
sti
;Section 2
pushf
assume ds:nothing
call Old_Keyboard_IO
nop
iret
new_keyboard_io endp
;Section 3
Initialize:
assume cs:cseg,ds:cseg
mov bx,cs
mov ds,bx
mov al,16h
mov ah,35h
int 21h
mov word ptr Old_Keyboard_IO,bx
mov word ptr Old_Keyboard_IO[2],es
;End Section 3
mov dx,offset new_keyboard_io
mov al,16h
mov ah,25h
int 21h
mov dx,offset Initialize
int 27h
cseg ends
end start
上面的程序中,第一部分是两个字(Double word),这是用来存放旧的键盘中断矢量.因为COM的程序都只限制在一个段中,因此数据段和代码段都在同一段中.而原先的中断处理程序和我们所编写的中断处理程序未必会在同一段中,所以必须使用双字来储存地址值.
双字Old_Keyboard_IO可以放在驻留程序中的任何地方;但是一般来说,放在Jmp Initialize 之后会比较方便;因为如果必须使用DEBUG来检查程序的话,可以比较容易调试.
上面程序中的第二部分是驻留程序的主体,其中包括了一个调用原先键盘中断处理程序的模拟中断.因为原先的键盘中断处理程序必须使用INT的方式调用,而不是使用CALL的指令调用;因此必须先使用PUSHF把CPU状态标志压入堆栈中,然后配合上CALL来模拟INT的动作.
注意一点,assume ds:nothing这一行是汇编指示,而不是程序代码.它是用来告诉汇编器在产生下一行机器码时,不要更会目前DS的内容;这样做才可以让汇编器为下一个指令产生双字的地址值.
当Call Old_Keyboard_IO指令执行时,控制权就转移到旧的键盘中断处理程序.而当这个中断调用执行完时,它就执行IRET指令,于是控制权又交还到目前的驻留程序.这样做,不但可以让原先的键盘中断程序包为我们工作,同时也可以掌握控制权.如果只使用IMP指令,跳到旧的键盘中断处理程序包去,而不把CPU状态标志推入堆栈中,那么一旦执行到IRET时,就真正返回到中断的状态.
上面程序中的第三部分是启动代码部分,在这一部分中,设定好新的中断矢量,同时把旧的中断矢量存放在驻留程序代码中,以便让驻留程序使用.
4.7 检查驻留程序
到目前为止,已经成功地把驻留程序加在应用程序和DOS的键盘输入之间;接下来可以修改输入的字符.在这一节中,我们准备截获键盘的输入,并且把"Y"改成"y","y"改成"Y".
以下是程序代码:
cseg segment
assume cs:cseg,ds:cseg
org 100h
start:
jmp Initialize
Old_Keyboard_IO dd ?
new_keyboard_io proc far
assume cs:cseg,ds:cseg
sti
;Section 1
cmp ah,0
je ki0
assume ds:nothing
jmp Old_Keyboard_IO
;Section 2
ki0:
pushf
assume ds:nothing
call Old_Keyboard_IO
cmp al,'y'
jne ki1
mov al,'y'
jmp kidone
ki1:
cmp al,'Y'
jne kidone
mov al,'y'
kidone:
iret
new_keyboard_io endp
;Section 3
Initialize:
assume cs:cseg,ds:cseg
mov bx,cs
mov ds,bx
mov al,16h
mov ah,35h
int 21h
mov word ptr Old_Keyboard_IO,bx
mov word ptr Old_Keyboard_IO[2],es
;End Section 3
mov dx,offset new_keyboard_io
mov al,16h
mov ah,25h
int 21h
mov dx,offset Initialize
int 27h
cseg ends
end start
在面的程序第一部分主要是检查AH是否等于0(读取字符).如果AH不等于0,就用旧的中断处理程序来处理其它的功能:1H(读取键盘状态),2H(读取键盘标志).在这里,使用JMP指令,而非使用CALL来模拟软件中断;因此原先的中断处理程序结束后,就直接返回到中断前的状态.
程序的第二部分是处理AH=0H时的情形.首先程序中断模拟一个软件中断来调用旧的键盘处理程序,是为了在读完字符之后,控制权能交还到我们的驻留程序,接下来的几行程序是检查读到的字符是不是"Y"和"y",如果是的话就修改它.
可以借执行这个程序,来验证其是否正确.除此之外,也可以证明,在操作系统和应用程
发表评论
-
用汇编写的病毒代码
2010-05-29 08:04 1898转自:http://www.programfan.com ... -
堆和栈(堆栈)的区别
2010-05-20 16:47 866转自:http://www.360doc.com/sho ... -
OllyDBG 入门系列(三)-函数参考
2010-05-06 14:07 1390转自:http://bbs.pediy.com/s ... -
OllyDBG 入门系列(二)-字串参考
2010-05-05 19:02 1142转自:http://bbs.pediy.com/s ... -
用汇编编写DOS下的内存驻留程序(5)
2010-05-05 11:05 1022转自:http://www.x86asm.com/Dis ... -
用汇编编写DOS下的内存驻留程序(3)
2010-05-05 11:03 896转自:http://www.x86asm. ... -
用汇编编写DOS下的内存驻留程序(2)
2010-05-05 11:02 767转自:http://www.x86asm. ... -
用汇编编写DOS下的内存驻留程序(1)
2010-05-05 11:01 1503转自:http://www.x86 ... -
补码 左移与右移 32位系统
2010-05-02 11:04 2796QQ:797801 计算机中 ... -
c语言控制语句对应的汇编语句代码
2010-04-29 18:15 2253转:http://blog.csdn.net/ ... -
调试windows程序
2010-03-21 10:11 814debug 只能支持1M的寻址 如果需要调 ... -
ds:[bp+2]
2009-11-28 13:22 907mov ax,0 mov ds ... -
程序的起始地址的段地址
2009-11-26 09:38 1325生成EXE之后用Debug加载后,查看寄存器内容如下: ... -
将内存FFFF:0--FFFF:b单元中的数据复制到0:200--0:20b单元中
2009-10-25 21:13 1677该程序的功能: 将内存FFFF:0--FF ... -
标志寄存器
2009-10-25 18:46 999方便记忆 OF 溢出(是/否) ... -
安全的一段内存空间 0:2000~0:02FF
2009-10-25 18:20 950看到120页了 我们要向内存里写东西 ... -
显示红心的汇编代码
2009-10-25 18:05 923assume cs:code code segment ... -
debug时 遇到loop 指令
2009-10-25 17:35 901debug时 遇到loop 指令 用 p指 ... -
Loop的使用
2009-10-23 21:06 1216看到107页了 王爽汇编第五章 Loop的使用 ... -
王爽汇编的第四章的一个小例子--待验证
2009-10-20 21:02 1171第四章的一些总结: 1. DOS系统是单任 ...
相关推荐
### 汇编语言下DOS内存驻留程序设计概览 #### 1. 内存驻留程序(TSR)概念解析 内存驻留程序,全称“Terminate and Stay Resident Program”(TSR),是一种在计算机系统中加载至内存、执行完毕后并不释放其占用的内存...
文件"用汇编编写DOS下的内存驻留程序.doc"可能会提供更具体的技术实现和示例代码,这对于深入理解这个主题非常有帮助。 总之,用汇编编写DOS下的内存驻留程序是一个对系统级编程有深入了解的过程。它涉及到汇编语言...
在这个模块中,我们使用dos下的中断函数和内存驻留函数来设置中断和内存驻留程序。 三、程序流程图 下面是屏保程序的流程图: 1. 初始化程序 2. 图形录入 3. 动画绘制 4. 非工作状态计时 5. 检测键盘输入 6. 中断...
通过这些文件,我们可以了解到这是一套使用汇编语言编写的内存驻留程序,可能涉及到键盘输入处理、中断服务以及程序自身的管理和激活机制。学习这些源代码可以帮助理解如何在DOS环境下有效地利用有限的内存资源,...
本文将深入探讨一个用汇编语言实现的常驻内存时钟程序,这个程序可以在计算机运行时持续显示当前时间,无需用户交互。 首先,让我们了解汇编语言的基础。汇编语言中的每条指令都对应于计算机硬件的特定机器码,...
本篇文章将深入探讨一个由汇编语言编写的时钟小程序,这个程序巧妙地利用了DOS中断,并且具有彩色显示功能,是作者为期末作业精心创作的作品。 首先,我们来理解汇编语言。汇编语言是一种符号化的机器语言,每个...
本课程设计报告的主要目的是设计和实现一个DOS下屏保程序,使用汇编语言编写该程序。该程序的主要功能是绘制屏保图形、计时、检测键盘输入和设置中断等。 汇编语言课程设计 汇编语言是一种低级编程语言,它直接...
标题中的“TSR程序作出DOS下的屏幕保护程序”指的是在DOS操作系统环境下,通过创建一个常驻系统内存(Terminable and Stay Resident, TSR)程序来实现屏幕保护功能的技术。这种程序可以在用户离开计算机或者一段时间...
在DOS环境中,开发者通常会使用汇编语言编写这样的程序,因为它们对内存管理和中断处理有更精细的控制。 **TSR程序的实现步骤** 1. **初始化**:TSR程序首先会进行一些基本的初始化工作,如分配内存、设置中断向量...
4. **编写时钟程序**:创建一个驻留在内存中的时钟程序,它能实时显示时间且不影响其他程序运行。这涉及到实时系统调用和中断处理,尤其是INT 1CH中断服务程序的修改和利用。 5. **画网格线**:根据用户输入的坐标...
第一部分是基础部分,以8086/8088为背景,以DOS和PC兼容机为软硬件平台,以MASM和TASM为汇编器,介绍汇编语言的有关概念,讲解汇编语言程序设计技术。第二部分是提高部分,以80386为背景,以新一代微处理器Pentium为...
用汇编语言(8086/DOS)实现一个显示系统时间的程序,运行时,在屏幕的右上角将显示本机系统的日期及时间,可以应用到其它适用环境中,实现其日期、时间自动更新显示功能。 知识点6:程序设计的步骤 设计的步骤包括...
`PLAY.ASM`可能是该程序的源代码,用汇编语言编写,这种低级语言直接对应机器指令,对于理解程序的工作原理非常有用。 在使用该音乐播放器时,有以下几个关键步骤: 1. **运行驻留内存的PLAY**:首先,用户需要在...
### DOS功能调用符与Debug程序调试详解 在学习计算机编程的过程中,特别是针对早期操作系统如DOS的理解,掌握DOS功能调用符...对于汇编语言学习者而言,熟练掌握这两方面知识对于理解和编写DOS下的应用程序至关重要。
- **简介**:本章介绍了如何使用汇编语言编写名为MACLIST的程序,该程序主要用于增强现有应用程序的功能。 - **功能介绍**: - 从键盘获取输入。 - 定位磁盘上的目录和文件。 - 对内存中的文件进行排序。 - 将...
实现这一步骤的必要性在于:一旦中断服务程序驻留内存后,一般程序员使用这一新增的中断调用就如同调用DOS或BIOS的中断子程序一样,只要了解其入口要求和返回参数就可调用。程序驻留在内存后,它占用的存储区就不会...
这个程序需要驻留在内存中,并在屏幕上固定位置(如右上角)显示时钟,同时不影响其他程序运行。利用INT 1CH中断服务程序,每0.05秒更新一次时间显示,获取系统实时钟信息。 5. **画网格线**: 程序需根据用户...
6. 内存管理:DOS在有限的内存资源下进行了有效的管理,如使用分配器(ALLOC.S)来分配内存,释放器(FREE.S)来回收内存。同时,通过扩展内存管理器(EMS和XMS)来利用扩展内存。 7. 多任务和并发:尽管DOS本身并...