在汇编原理2:源代码开始的博客中,我们可以看到fasm源代码的最开始的地方有一个ah=4ah时的int 21h调用。本文章就来分析一下这个调用有什么作用。
我们先来看一个例子:
;使用masm5.0进行编译;程序功能:没什么功能?
;注意:使用masm5.编译asm文件的时候,文件名长度不要超过8个字符,否则会无法打开文件.
assume cs:code,ds:data
data segment
msgdb 'I have a large buffer--32KB--ha!ha!',24h
buffer db 32768 dup (?)
data ends
code segment
start:mov ax,data
mov ds,ax
mov ah,9
mov dx,offset msg;ah=9,ds:dx指向由美元标志($,对应于ASCII码24h)终止的字符串.
int 21h;将字符串输出给标准输出设备(STDOUT)
mov ah,4ch
int 21h
codeends
end start
程序中我们使用db伪指令在数据段开辟了一个32KB的缓冲区,这个缓冲区可能会被程序使用。但是使用masm5.0编译后,我们发现,生成的exe文件居然有33344个字节那么大。但是这样编程这样会显著增加可执行文件的长度。如果这样的情况比较多,那么会生成很多比较大的exe文件,浪费磁盘空间。
如何解决这个问题呢?我们知道操作系统的一个重要特征就是可以对各种资源惊醒有效的管理。这个的资源指的是各种数据(文件)及系统安装的各类设备。毫无疑问内存也属于设备,它页应该受到操作系统很好的管理。
DOS就具备管理内存的能力。应用程序可以通过系统功能调用向DOS申请一些内存,结束之前再通过系统功能把借来的内存还给DOS。从而可以不用db伪指令在数据段里定义缓冲区。
int 21h的48H功能就是DOS提供的用于申请内存的功能调用。看下面这个例子:
;使用masm5.0进行编译;程序功能:没什么功能?;注意:使用masm5.编译asm文件的时候,文件名长度不要超过8个字符,否则会无法打开文件.;此程序中,使用int 48h分配内存会失败。assume cs:code,ds:datadata segmentmsgdb 'I have a large buffer--32KB--ha!ha!',0dh,0ah,24herr1db 'memory control blocks are destroyed',0dh,0ah,24herr2db 'not enough memory',0dh,0ah,24hdata endscode segmentstart:mov ax,datamov ds,axmov ah,9mov dx,offset msg;ah=9,ds:dx指向由美元标志($,对应于ASCII码24h)终止的字符串.int 21h;将字符串输出给标准输出设备(STDOUT)mov ah,48hmov bx,10int 21hjnc exit;如果分配成功,程序就退出cmp ax,07hje error1cmp ax,08hje error2jmp exiterror1:mov ax,datamov ds,axmov ah,9mov dx,offset err1int 21hjmp exiterror2:mov ax,datamov ds,axmov ah,9mov dx,offset err2int 21hexit:mov ah,4chint 21hcodeendsend start;48H功能就是DOS提供的用于申请内存的功能调用: ;功能号:48H;用 途:向DOS申请一定量连续内存空间;参 数:AH=48H; BX=申请内存的"节"数,每一节是16个字节;调 用:INT 21H;返 回:如果成功,则CF清零,AX=申请到的内存块段地址; 如果失败,则CF置1,AX=错误代码,BX=当前内存最大可用块的大小; AX=07H 内存控制块被破坏; 08H 内存不够 ;注意:一"节"内存包含16个连续的BYTE,但这16个BYTE的起始地址必须是16的整数倍。比如从0B800H:0000H处开; 始的16个BYTE可以看作是一节,而从0B800H:0001H处开始的16个字节就不是一节。; 如果这个功能正确地执行了,则DOS将通过AX寄存器返回这块内存的段地址,偏移地址就是0。
这个程序中,理想情况下,通过调用int 21h的48h功能,我们可以获得256个字节的内存。但是通过运行程序,我们可以发现,程序提示:not enough memory。这是怎么一回事呢?难道DOS真的连256个字节的空闲内存也没有了吗?
程序本身并没有什么问题。问题出在DOS身上,因为它太“懒惰”。大家都知道用户程序的长度是不确定的。长的有几百KB,短的可能只有几个Byte。按理说DOS在调入一个程序时应该先确定这个程序的长度。然后再按程序的长度给它分配内存,但是DOS并没有这么做。无论程序有多么长,它都是一股脑的把所有的*内存都分给这个程序。所以系统虽然具有几倍KB的*内存,但是当某程序调入内存运行时,这几百KB的*内存都成了它的“私有财产”,所以当它再次申请内存时DOS已经没有空闲内存了。因此我们调用int 21h的48h功能会失败。
因此我们说,当一个应用程序在运行时,如果想要申请内存,应该首先按照自己需要的实际长度把DOS给它的内存给重新分配一下,自己占了多少得救保留多少,自己不占用的内存就要“无私奉献”出来。这样才能想DOS申请内存,这就是所谓的“索取”之前先要“奉献”。
DOS提供了一个专门用于重新分配内存的功能,这就是我们开始提到的int 21h的48h号功能。看下面这个例子:
;使用masm5.0进行编译;程序功能:没什么功能?;注意:使用masm5.编译asm文件的时候,文件名长度不要超过8个字符,否则会无法打开文件.;此程序中,使用int 48h分配内存会失败。assume cs:code,ds:datadata segmentmsgdb 'reallocate memory failed',0dh,0ah,24herr1db 'memory control blocks are destroyed',0dh,0ah,24herr2db 'not enough memory',0dh,0ah,24hdata endscode segmentstart:mov ah,4ahmov bx,800h;32kint 21h;重新分配本程序占用的内存jnc nextmov ax,datamov ds,axmov ah,9mov dx,offset msg;ah=9,ds:dx指向由美元标志($,对应于ASCII码24h)终止的字符串.int 21h;将字符串输出给标准输出设备(STDOUT)jmp exitnext:mov ah,48hmov bx,9000h;576kint 21hjnc exit;如果分配成功,程序就退出cmp ax,07hje error1cmp ax,08hje error2jmp exiterror1:mov ax,datamov ds,axmov ah,9mov dx,offset err1int 21hjmp exiterror2:mov ax,datamov ds,axmov ah,9mov dx,offset err2int 21hexit:mov ah,4chint 21hcodeendsend start;48H功能就是DOS提供的用于申请内存的功能调用: ;功能号:48H;用 途:向DOS申请一定量连续内存空间;参 数:AH=48H; BX=申请内存的"节"数,每一节是16个字节;调 用:INT 21H;返 回:如果成功,则CF清零,AX=申请到的内存块段地址; 如果失败,则CF置1,AX=错误代码,BX=当前内存最大可用块的大小; AX=07H 内存控制块被破坏; 08H 内存不够 ;注意:一"节"内存包含16个连续的BYTE,但这16个BYTE的起始地址必须是16的整数倍。比如从0B800H:0000H处开; 始的16个BYTE可以看作是一节,而从0B800H:0001H处开始的16个字节就不是一节。; 如果这个功能正确地执行了,则DOS将通过AX寄存器返回这块内存的段地址,偏移地址就是0
这个例子中,我们在程序开始的地方调用int 21h的4ah功能给程序重新分配了32k的内存,然后后面调用int 21h的48h功能分配了576k的内存,也分配成功了。
看到这里,大家应该明白了吧。fasm源代码的最开始的地方调用int 21h的4ah功能就是为了给程序重新分配内存空间。因为后面会调用int 21h的48h功能动态地向DOS申请内存,如果不在前面调用4ah功能重新分配内存,后面申请内存肯定会失败的。呵呵
参考:PC机汇编语言实战精解 第11章