217页
程序的主要部分后面是主程序所使用的许多小的扩充内存功能。将这些功能组合起
来这些功能便覆盖了扩充内存的操作,尽管还可能想向它们添加错误检查。
程序所包含的函数有:
emmtest 检验内存中是否存在扩充内存管理程序。
emmok 检验扩充内存管理程序的功能并确定页框架的基地址。
emmalloc 从扩充内存管理程序请求指定数量的页数。
emmmap 将一个扩充内存页映射进页框架中。
emmmove 把数据移进页框架中的指定页中。
emmget 从页框架中的指定页中获得数据。
emmclose 将扩充内存句柄的控制返回给扩充内存管理程序。
扩充内存的简单应用程序能用这些基本功能来启动,然后适当地增加错误检查和新
的功能,就可以发展成羽翼丰满的程序。
10.4扩展内存
扩展内存是指超过8086的1M限制的内存。在扩展内存规范(XMS)的范围内,扩展
内存还指高位内存区(HMA)和上位内存块(UMB)。
UMB是DOS640K内存界线与8086 1M界线之间的有用内存区域。它们通常不与
640K有用内存相连接,并且在DOS5.0之前,它们只有通过XMS驱动程序才对程序员
有用。从DOS5.0开始,UMB能通过DOS的内存服务来访问;DOS内存服务则可为你访
问XMS驱动程序。
HMA是一个奇怪的东西,在其中当CPU位于实地址方式中时,第21个地址线(A20
线)被激活,增加65520个字节,增加可用内存的数量,所增加的内存在实地址方式中的
CPU也能直接访问它。这个过程的工作方式如下:考察一下段值加上偏移值对物理内存
的映射,就会发现段值乘以16再加偏移值,就是所访问的物理地址。如果这个值超过20
位,那么就丢掉较高的几位,将物理地址映射在000000H到0FFFFFH的范围内。于是,地
址0FFFF:0010H在A20为0时,映射的物理地址是000000H,在A20为1时,映射的物
理地址是0100000H。额外的65520个内存字节正是来自这里;从0FFFF : 0010H到
0FFFF : FFFFH这些地址通常映射到物理地址000000H到00FFEFH上,而在这里,它
们映射到了物理地址010000H到010FFEFH上。
XMS驱动程序提供了5组服务:驱动程序信息功能;HMA管理功能;A20管理功
能;扩展内存管理功能以及上位内存管理功能。两个另外的服务确定XMS驱动程序是否
存在并且获得XMS驱动程序的控制功能的地址。
10.4.1确定扩展内存的有效性
要确定XMS驱动程序是否存在,可以执行下列代码:
mov AX,04300H
218页
int 02FH ·=
cmp AL, 080H = ::
jne XMS_NotPresent
;XMS is present
如果XMS存在,就需要获得XMS驱动程序的控制功能的地址。这可借助下列代码
片段来完成:
code segment
mov AX,04310H ”
int 02FH
mov word ptr[XMS_Control],BX
mov word ptr[XMS_COntrol],ES
cOde ends
data segment
XMS_Control dd (?)
data ends
10.4.2使用扩展内存
如果扩展内存可用,就可以用扩展内存功能来获得和使用扩展内存(XMS参考手册
一节位于本书的第5部分,那里介绍了扩展内存功能)。本节中的两个列表是扩展内存功
能方面的简单实例。
列表10.7中的程序分析了扩展内存的基本操作。如下所示:
·检验一下扩展内存是否存在。
·获得XMS版本号,检查一下HMA是否存在,并获得扩展内存最大空白块的大
小。
·试着分配一个扩展内存块。
·向扩展内存块写数据。
·从扩展内存块读回数据。
·释放扩展内存块。
·试着分配一个上位内存块。
·释放上位内存块。
这是在扩展内存中的基本操作。它们使你能在多个程序中运行内存区。可用这类技
术将数据放入扩展内存中。
列表10.7
/*xmstest.C
Listing 10.7 of DOS Programmer's Reference*/
#include<ctype.h>
#include<dos.h>
219页
#include<stdiO.h>
#include<mem.h>
Struct XMS_Status{
unsigned short version;
unsigned short revision;
unsigned short Hma_existence;
};
Struct XMS_memory_StatuS{
unsigned short largest_block;
unSigned ShOrt tOtal_free_memOry;
unsigned short error_status;
};
struct bstat{
unSigned Short lock_count;
unSigned shOrt free_handle_count;
};
union memory_address{
unSigned long long_offset;
struct conventional_address{
unsigned short conventional_offset;
unsigned short conventional_segment;
} C_a;
};
Struct XMS_move_data{
unsigned long transfer_size;
unsigned short source_handle;
UniOn memOry_address sOurce_OffSet;
unsigned short deStination_handle;
union memory_addreSS deStination_offset;
};
Struct umb_data{
unsigned short segment_number;
unsigned short segment_size;
};
extern unsigned short far
a20_stat(void);
extern unsigned short far
alter_a20(unSigned Short change_code);
extern unsigned short far
hma_alloc(unsigned ShOrt size_in_byteS);
extern unsigned short far
hma_free(void);
extern void far
hma_move(unsigned short count, unsigned short direction,
unsigned short hma_offset, vOid far*data);
extern unsigned short far
umb_alloc(unsigned short size struct umb data far*ptr);
extern unsigned short far
umb_free(unsigned short segment);
extern unsigned short far
xms_alloc(unsigned short size,unsigned short far*handle);
extern void far
xms_avail(struct XMS_memOry_StatuS far*ptr);
extern unsigned short far
xms_bstat(unsigned short handle, struct bstat far*ptr);
extern unsigned short far
220页
xms_free(unsigned short handle);
extern unsigned short far
xms_lock(unsigned short handle, unsigned long *linear_address);
extern unsigned short far
xms_move(Struct XMS_move_data far *ptr);
extern unsigned short far
xms_realloc(unsigned short handle, unsigned short size);
extern void far
xms_stat(struct XMS_status far *ptr);
extern unsigned short far
xms_test(void);
extern unsigned short far
xms_unlock(unsigned short handle);
/* Return Codes */
#define A20_DISABLED (0*0000)
#define A20_ENABLED (0*0001)
#define FAILURE (0*0000)
#define SUCCESS (0*0001)
#define FUNCTION_NOT_IMPLEMENTED (0*0080)
#define VDISK_DEVICE_DETECTED (0*0081)
#define A20_ERROR_OCCURRED (0*0082)
#define HMA_DOES_NOT_EXIST (0*0090)
#define HMA_ALREADY_IN_USE (0*0091)
#define NOT_ENOUGH_BYTES_TO_ALLOC_HMA (0*0092)
#define HMA_NOT_ALLOCATED (0*0093)
#define A20_LINE_STILL_ENABLED (0*0094)
#define ALL_EXTENDED_MEMORY_ALLOCATED (0*00A0)
#define ALL_EXTENDED_MEMORY_HANDLES_USED (0*00A1)
#define INVALID_HANDLE (0*00A2)
#define INVALID_SOURCE_HANDLE (0*00A3)
#define INVALID_SOURCE_OFFSET (0*00A4)
#define INVALID_DESTINATION_HANDLE (0*00A5)
#define INVALID_DESTINATION_OFFSET (0*00A6)
#define INVALID_LENGTH (0*00A7)
#define INVALID_OVERLAP (0*00A8)
#define PARITY_ERROR (0*00A9)
#define BLOCK_NOT_LOCKED (0*00AA)
#define HANDLE_IS_LOCKED (0*00AB)
#define LOCK_COUNT_OVERFLOW (0*00AC)
#define LOCK_FAILED (0*00AD)
#define SMALLER_UMBAVAILABLE (0*00B0)
#define NO_UMBS_AVAILABLE (0*00B1)
#define INVALID_UMB_SEGMENT (0*00B2)
#define ILLEGAL_PARAMETER (0*FFFF)
/* Codes for alter_a20() */
#define GLOBAL_A20_LINE (0)
#define LOCAL_A20_LINE (2)
#define ENABLE_A20_LINE (0)
#define DISABLE_A20_LINE (1)
/* Codes for hma_move() */
#define COPY_FROM_HMA (1)
#define COPY_TO_HMA (0)
#define KILOBYTE (1024)
unsigned char test_buffer[KILOBYTE];
void test_hma(void)
221页
{
int HMA_allocated;
int A20_status;
int original_a20_status;
unsigned short result;
HMA_allocated=0;
result = hma_alloc(0*FFF0);
switch(result)
{
case SUCCESS:
printf("HMA successfully allocated to current process\n");
HMA_allocated++;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("HMA allocate not implemented by XMS\n");
break;
Case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; couldn't allocate HMA\n");
break;
Case HMA_DOES_NOT_EXIST:
printf("HMA cannot be allocated; doesn't exist\n");
return;
case HMA_ALREADY_IN_USE:
printf("HMA cannot be allocated; already in use\n");
break;
case NOT_ENOUGH_BYTES_TO_ALLOC_HMA:
printf("Not enough bytes to allocate HMA were specified\n");
return;
default:
printf("Invalid result of hma_alloc(): 0*%.4X\n", result);
return;
}
if(HMA_allocated)
{
Original_A20_Status = A20_StatuS = -1;
result = a20_stat();
switch(result)
{
case A20_DISABLED:
original_A20_Status=A20_Status = 0;
break;
case A20 ENABLED:
Original_A20_Status = A20_Status = 1;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Can't get A20 line status; function not implemented\n");
break;
case VDISK_DEVICE_DETECTED:
printf("Can't get A20 line Status; VDISK device detected\n");
break;
default:
printf("Can't get A20 line Status; unexpected result 0*%.4X\n",
result);
break;
}
if(A20_status == 0) /* Disabled */
{
result = alter_a20(GLOBAL_A20_LINE|ENABLE_A20_LINE);
switch(result)
{
case SUCCESS:
222页
printf("A20 line globally enabled\n");
A20_status=1;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented; couldn't enable A20 line\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; couldn't enable A20 line\n");
break;
case A20_ERROR_OCCURRED:
printf("Error occurred while attempting to enable A20 line\n");
break;
case ILLEGAL_PARAMETER:
printf("Illegal argument to alter_a20()\n");
break;
default:
printf("Unexpected result 0*%.4X from alter_a20()\n", result);
break;
}
}
if(A20_status==1)
{
int k;
memset(test_buffer,0*55,KILOBYTE);
hma_move(KILOBYTE,Copy_to_HMA,0,test_buffer);
memset(test_buffer, 0*AA, KILOBYTE);
hma_move(KILOBYTE,copy_FROM_HMA,0,test_buffer);
result=1;
for(k=0; k<KILOBYTE; k++)
{
if(test_buffer[k] != 0*55)
{
printf("Error transferring data to/from HMA\n");
result=0;
break;
}
}
if(result)
{
memset(test_buffer, 0*AA, KILOBYTE);
hma_move(KILOBYTE,COPY_TO_HMA,0,test_buffer);
memset(test_buffer,0*55, KILOBYTE);
hma_move(KILOBYTE, COPY_FROM_HMA,0,test_buffer);
for(k=0; k<KILOBYTE; k++)
{
if(test_buffer[k] != 0*AA)
{
printf("Error transferring data to/from HMA\n");
result=0;
break;
}
}
}
if(result)
printf("Transfer of data to/from HMA test successful\n");
if(original_A20_status==0)
{
result=alter_a20(GLOBAL_A20_LiNE|DISABLE_A20_LINE);
switch(result)
{
case SUCCESS:
223页
printf("A20 line glObally disabled\n");
break;
Case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented, disable A20 line\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; disable A20 line\n");
break;
Case A20_ERROR_OCCURRED:
printf("Error while attempting to disable A20 line\n");
break;
Case ILLEGAL_PARAMETER:
printf("Illegal argument to alter_a20()\n");
break;
case A20_LINE_STILL_ENABLED:
printf("A20 line still enabled\n");
break;
default:
printf("Unexpected result 0*%.4X from alter_a20()\n ",
result);
break;
}
}
}
result=hma_free();
switch(result)
{
Case SUCCESS:
printf("HMA successfully freed\ n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("HMA free not implemented by XMS\n");
. break; .
case VDISK_DEViCE_DETECTED:
printf("VDISK device detected; couldn't free HMA\n");
break;
Case HMA_DOES_NOT_EXIST:
printf("HMA does not exist\n");
return;
case HMA_NOT_ALLOCATED:
printf("HMA was not allocated\n");
return;
default:
printf("Invalid result of hma_fRee(): 0*%.4X\n", result);
return;
}
}
else
{
Original_A20_status=A20_status=-1;
result=a20_stat();
switch(result)
{
case A20_DISABLED:
original_A20_status = A20_status = 0;
break;
case A20_ENABLED:
original_A20_status=A20_status=1;
break;
Case FUNCTION_NOT_IMPLEMENTED:
printf("Can't get A20 line status; function not implemented\n");
break;
case VDISK_DEVICE_DETECTED:
224页
printf("Can't get A20 line Status; VDISK device detected\n");
break;
default:
printf("Can't get A20 line Status; unexpected result 0*%.4X\n", result);
break;
}
if(A20_StatuS == 0) /* Disabled */
{
result = alter_a20(GLOBAL_A20_LINE|ENABLE_A20_LINE);
switch(result)
{
case SUCCESS:
printf("A20 line globally enabled\n");
A20_Status=1;
break;
Case FUNCTION_NOT_IMPLEMENTED:
printf( "Function not implemented; Couldn't enable A20 line\n" ) ;
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; couldn't enable A20 line\n");
break;
case A20_ERROR_OCCURRED:
printf("Error occurred while attempting to enable A20 line\n");
break;
Case ILLEGAL_PARAMETER:
printf("Illegal angument to alter_a20()\n");
break;
default:
printf("Unexpected result 0*%.4X from alter_a20( )\n", result);
break;
}
}
if(A20_statuS == 1) /* Engbled */
{
int k;
int m;
memset(test_buffer, 0, KILOBYTE);
hma_move (KILOBYTE, COPY_FROM_HMA, 0, test_buffer);
for(k=0; k<(KILOBYTE/16); k++)
{
printf("HMA:%.3X0", k);
for(m=0; m<16; m++)
printf("%.2X", test_buffer[(k<<4) + m]);
for(m=0; m<16; m++)
{
if(isprint(test_buffer[(k<<4) + m]))
printf("%c", test_buffer[(k<<4) + m]);
else
printf(".");
}
printf("\n");
}
if(original_A20_Status==0)
{
result = alter_a20 (GLOBAL_A20_LINE | DISABLE_A20_LINE);
switch(result)
{
case SUCCESS:
printf("A20 line globally disabled\n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented; disable A20 line\n");
225页
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected; disable A20 line\n");
break;
case A20_ERROR_OCCURRED:
printf("Error while attempting to disable A20 line\n");
break;
case ILLEGAL_PARAMETER:
printf("Illegal argument to alter_a20()\n");
break;
case A20_LINE_STILL_ENABLED:
printf("A20 line still enabled\n");
break;
default:
printf("Unexpected result 0*%.4X from alter_a20()\n",
result);
break;
}
}
}
}
}
void test_xms(void)
{
struct XMS_memory_status status;
struct bstat block_status;
struct XMS_move_data move_block;
unsigned short handle;
unsigned short result;
unsigned long address;
int move_Ok;
xms_avail(&status);
printf("Largest block of XMS memory available = %.4X Kbytes\n",
status.largest_block);
printf("Total free memory = %.4X Kbytes\n",
status.total_free_memory);
printf("Error status = %.4X\n", status.error_status);
if(status.error_status)
return;
result = xms_alloc(status.largest_block, &handle);
switch(result)
{
case SUCCESS:
printf("Memory allocated; handle = %.4X\n", handle);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_alloc\n");
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_alloc\n");
return;
case ALL_EXTENDED_MEMORY_ALLOCATED:
printf("All memory allocated: xms_alloc\n");
return;
case ALL_EXTENDED_MEMORY_HANDLES_USED:
printf("All memory handles in use: xms_alloc\n");
return;
default:
printf("Unexpected result from xms_alloc: %.4X\n", result);
return;
}
226页
result = xms_realloc(handle, status.largest_block/2);
switch(result)
{
case SUCCESS:
printf("Block successfully reallocated in half\n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("xms_realloc not implemented\n");
break;
Case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_realloc\n");
break;
Case ALL_EXTENDED_MEMORY_ALLOCATED:
printf("xms_realloc failed: all extended memory allocated\n");
break;
case ALL_EXTENDED_MEMORY_HANDLES_USED:
printf("xms_realloc failed: all extended memory handles in use\n");
break;
Case INVALID_HANDLE:
printf("xms_realloc failed: invalid handle\n");
break;
case HANDLE_IS_LOCKED:
printf("xms_realloc failed: handle is locked\n");
break;
default:
printf("UnexpeCted result from xms_realloc: 0*%.4X\n", result);
break;
}
memset(test_buffer, 0*55, KILOBYTE);
move_block.transfer_size = KILOBYTE;
move_block.source_handle = 0;
move_block.source_offset.c_a.conventional_offset = FP_OFF((void far*)test_buffer);
move_block.source_offset.c_a.conventional_segment=FP_SEG((void far*)test_buffer);
move_block.destination_handle = handle;
move_block.destination_offset.long_Offset = 0L;
result = xms_move(&move_block);
move_ok = 0;
switch(result)
{
case SUCCESS:
printf("Data successfully written to XMS\n");
move_ok=1;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("xms_move not implemented\n");
break;
Case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_alloc\n");
break;
Case INVALID_source_HANDLE:
printf("Invalid source handle\n");
break;
case INVALID_SOURCE_OFFSET:
printf("Invalid source Offset\n");
break;
case INVALID_DESTINATION_HANDLE:
printf("Invalid destination handle\n");
break;
Case INVALID_DESTINATION_OFFSET:
printf("Invalid destination offset\n");
break;
Case INVALID_LENGTH:
printf("Invalid Offset\n");
break;
227页
case INVALID_OVERLAP:
printf("Invalid overlap\n");
break;
case PARITY_ERROR:
printf("Parity Error\n");
break;
default:
printf("Unexpected result from xms_move(): 0*%.4X\n");
break;
}
if(move_ok)
{
memset(test_buffer, 0*AA, KILOBYTE);
move_block.transfer_size = KILOBYTE;
move_block.source_handle = handle;
move_block.source_offset.long_offset=0L;
move_block.destination_handle = 0;
move_block.destination_offset.c_a.conventional_offset =
FP_OFF((void far*)test_buffer);
move_block.destination_offset.c_a.conventional_segment=
FP_SEG((void far*)test_buffer);
result = xms_move(&move_block);
move_ok=0;
switch(result)
{
case SUCCESS:
printf("Data successfully read from XMS\n");
move_Ok = 1;
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("xms_move not implemented\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_alloc\n");
break;
Case INVALID_SOURCE_HANDLE:
printf("Invalid source handle\n");
break;
Case INVALID_SOURCE_OFFSET:
printf("Invalid source offset\n");
break;
Case INVALID_DESTINATION_HANDLE:
printf("Invalid destination handle\n");
break;
case INVALID_DESTINATION_OFFSET:
printf("Invalid destination offset\n");
break;
case INVALID_LENGTH:
printf("Invalid offset\n");
break;
case INVALID_OVERLAP:
printf("Invalid overlap\n");
break;
case PARITY_ERROR:
printf("Parity Error\n");
break;
default:
printf("Unexpected result from xms_move():0*%.4X\n");
break;
}
if(move_Ok)
{
int k;
228页
for(k=0; k<KILOBYTE; k++)
{
if(test_buffer[k] != 0*55)
{
printf("Error: corrupted data at Offset 0*%.4X; data = %.2X\n",
k, test_buffer[k]);
move_ok=0;
}
}
if(move_ok)
printf("Data read from XMS verified OK\n");
}
}
result = xms_lock(handle, &address);
switch(result)
{
case SUCCESS:
printf("Block locked; linear address = %.81X\n", address);
break;
Case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented: xms_lock\n");
break;
case VDISK_DEViCE_DETECTED:
printf("VDISK device detected: xms_lock\n");
break;
Case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
Case LOCK_COUNT_OVERFLOW:
printf("Lock count overflowed\n");
break;
case LOCK_FAILED:
printf("Lock failed\n");
break;
default:
printf("Unexpected result from xms_lock: %.4X\n", result);
break;
}
result = xms_free(handle);
switch(result)
{
case SUCCESS:
printf("XMS memory freed\n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented: xms_free\n");
break;
Case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_free\n");
break;
Case INVALID_HANDLE:
printf("Handle iS invalid\n");
break;
Case HANDLE_IS_LOCKED:
printf("Handle iS locked\n");
break;
default:
printf("Unexpected result from xms_free: %.4X\n", result);
break;
}
result = xms_bstat(handle, &block_status);
switch(result)
{
case SUCCESS:
229页
printf("LOCK count = %d\n", block_status.lock_count);
printf("Free handle count = %d\n", block_status.free_handle_count);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented: xms_bstat\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_bstat\n");
break;
Case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
default:
printf("Unexpected result from xms_bstat: %.4X\n", result);
break;
}
result = xms_lock(handle, &address);
switch(result)
{
case SUCCESS:
printf("Block locked; linear address = %.81X\n", address);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented: xms_lock\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_lock\n");
break;
case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
case LOCK_COUNT_OVERFLOW:
printf("Lock count overflowed\n");
break;
case LOCK_FAILED:
printf("LOCK failed\n");
break;
default:
printf("Unexpected result from xms_lock: %.4X\n", result);
break;
}
result = xms_bstat(handle, &block_status);
switch(result)
{
case SUCCESS:
printf("LOCK count = %d\n", block_status.lock_count);
printf("Free handle count = %d\n", block_status.free_handle_count);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented: xms_bstat\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_bstat\n");
break;
case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
default:
printf("Unexpected result from xms_bstat: %.4X\n", result);
break;
}
result = xms_unlock(handle);
switch(result)
{
case SUCCESS:
230页
printf("Block unlocked\n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_unlock\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected:xms_unlock\n");
break;
case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
case BLOCK_NOT_LOCKED:
printf("Block was not locked\n");
break;
default:
printf("Unexpected result from xms_unlock:%.4X\n",result);
break;
}
result=xms_free(handle);
switch(result)
{
case SUCCESS:
printf("XMS memory freed\n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_free\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device deteCted:xms_free\n");
break;
case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
case HANDLE_IS_LOCKED:
printf("Handle is locked\n");
break;
default:
printf("Unexpected result from xms_free:%.4X\n",result);
break;
}
result=xms_bstat(handle,&block_status);
switch(result)
{
case SUCCESS:
printf("Lock count=%d\n",block_status.lock_count);
printf("Free handle count=%d\n",block_status.free_handle_count);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_bstat\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected:xms_bstat\n");
break;
case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
default:
printf("Unexpected result frOm xms_bstat:%.4X\n",result);
break;
}
result=xms_unlock(handle);
switch(result)
231页
{
case SUCCESS:
printf("Block unlocked\n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_unlock\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected:xms_unlock\n");
break;
case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
case BLOCK_NOT_LOCKED:
printf("Block was not locked\n");
break;
default:
printf("Unexpected result from xms_unlock:%.4X\n",result);
break;
}
result=xms_bstat(handle,&block_status);
switch(result)
{
case SUCCESS:
printf("Lock count=%d\n",block_status.lock_count);
printf("Free handle Count=%d\n",block_status.free_handle_count);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("FunctiOn not implemented:xms_bstat\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected:xms_bstat\n");
break;
case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
default:
printf("Unexpected result from xms_bstat:%.4X\n",result);
break;
}
result=xms_free(handle);
switch(result)
{
case SUCCESS:
printf("XMS memory freed\n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("Function not implemented:xms_free\n");
break;
case VDISK_DEVICE_DETECTED:
printf("VDISK device detected: xms_free\n");
break;
case INVALID_HANDLE:
printf("Handle is invalid\n");
break;
Case HANDLE_IS_LOCKED:
printf("Handle is locked\n");
break;
default:
printf("Unexpected result from xms_free:%.4X\n",result);
break;
}
}
232页
void test_umb(void)
{
unsigned short status;
unsigned short size;
struct umb_data umb;
status=umb_alloc(0xFFFF,&umb);
switch(status)
{
case SUCCESS:
printf("We somehow allocated 1Mb of UMBs!\n");
return;
case FUNCTION_NOT_IMPLEMENTED:
printf("UMB alloc not implemented...\n");
return;
case SMALLER_UMB_AVAILABLE:
size=umb.segment_size;
printf("Smaller UMB available:0x%.4X paragraphs\n",size);
break;
case NO_UMBS_AVAILABLE:
printf("Sorry,no UMBS available\n");
return;
default:
printf("Unexpected result from umb_alloc():0x%.4X\n",status);
return;
}
status=umb_alloc(size, &umb);
switch(status)
{
case SUCCESS:
printf("0x%.4X paragraphs allocated at %.4X:0000\n",umb.Segment_size
,
umb.segment_number);
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("UMB alloc not implemented...\n");
return;
case SMALLER_UMB_AVAILABLE:
size=umb.segment_size;
printf("Smaller UMB available:0x%.4X paragraphs\n",size);
return;
case NO_UMBS_AVAILABLE:
printf("Sorry,no UMBs available\n");
return;
default:
printf("Unexpected result from umb_alloc():0x%.4X\n",status);
return;
}
status=umb_free(umb.segment_number);
switch(status)
{
case SUCCESS:
printf("UMB successfully freed\n");
break;
case FUNCTION_NOT_IMPLEMENTED:
printf("umb_free not implemented...\n");
break;
case INVALID_UMB_SEGMENT:
printf("Invalid Segment\n");
break;
default:
printf("Unexpected result from umb_free:0x%.4X\n",status);
break;
}
}
233页
int main()
{
unsigned short result;
struct XMS_status status;
int k;
int m;
result=xms_test();
switch(result)
{
case SUCCESS:
printf("XMS Detected\n");
break;
Case FAILURE:
printf("XMS Not Detected\n");
return 1;
default:
printf("Invalid result of xms_test(): 0x%.4X\n",result);
return 1;
}
xms_stat(&status);
printf("xms Version: %.2X.%.2x\n", status.version>>8,
status.version&0x00FF);
printf("XMS Driver Revision:%.2X.%.2x\n", Status.revision>>8,
status.revision&0x00FF);
printf("HMA existence: %.4X\n",status.HMA_existence);
if(status.HMA_existence==1)
test_hma();
test_xms();
test_umb();
return 0;
}
列表10.8是一个小小的函数库,它含有在列表10.7中所使用的扩展内存函数。经过
组合后的这些函数覆盖了各种扩展内存的操作。下面列出了此库中所包含的函数:
xms_test 测试扩展内存管理程序是否存在
xms_stat 获取扩展内存管理程序的版本号并检查HMA是否存在
xms_avail 获取最大的自由扩展内存块的大小
xms_alloc 向扩展内存管理程序请求一个扩展内存块
xms_realloc 重新分配一个扩展内存块,改变其大小
xms_lock 锁定一个扩展内存块
xms_unlock 解锁一个扩展内存块
xms_bstat 获取一个扩展内存块的状态
xms_move 往一个扩展内存块中移入数据或从中移出数据
xms_free 返回一个扩展内存块给扩展内存管理程序
hma_alloc 分配HMA
hma_free 释放HMA
hma_move 往HMA中移入数据或从中移出数据
alter_a20 允许或禁止A20线
a20_stat 获取A20线的状态
umb_alloc 分配一个UMB
umb_free 释放一个UMB
一些简单的扩展内存应用程序可将这些函数作为起点,并在此基础上添加出错检查,
234页
或添加一些新的功能。
列表10.8
page 55 ,132
; XMSLib.asm
; XMS library functions
XMS_CODE SEGMENT'CODE'
public _xms_test
public _xms_stat
public _xms_avail
public _xms_alloc
public _xms_realloc
public _xms_lock
public _xms_unlock
public _xms_bstat
public _xms_move
public _xms_free
public _hma_alloc
public _hma_free
public _hma_move
Public _alter_a20
public _a20_stat
public _umb_alloc
public _umb_free
ASSUME CS:XMS_CODE
ASSUME DS:NOTHING
ASSUME ES:NOTHING
ASSUME SS:NOTHING
;Storage for XMS call address
XMS_Control LABEL DWORD
XMS_Control_offset dw (?)
XMS_Control_Segment dw (?)
; Test for the presence of the XMS driver and,if present,
; get the address of the XMS function call
; Returns AX=1 if XMS found;otherwise AX=0
; Borland C++ prototype:unsigned short far xms_test(void);
ALIGN 16
_xms_test PROC FAR
push ES ; Save ES
mov AX,04300H ; See if XMS is present
int 2FH
cmp AL,080H ; if it is,AL=80H
jne __x_t2
; XMS is present;get the function address
mov AX,04310H
int 2FH
mov CS:[XMS_Control_offset],BX
mov CS:[XMS_Control_Segment],ES
ASSUME DS:NOTHING
235页
mov AX,1 ;Return 1
;Return. . .
__x_t1:
pop ES ; Restore ES
ret
; Return error
__x_t2:
xor AX,AX ; Return 0
jmp __X_t1
xms test ENDP
; Get XMS status
; Stores the XMS version number, driver revision number,
; and HMA existence flag at the address on the stack
; Borland C++ prototype:
; void far xms_stat(struct XMS_status far*ptr);
; Data structure:struct XMS_status
;{
; unsigned short version
; unsigned short revision;
; unsigned short HMA_existence;
};
ALIGN 16
_xms_stat PROC FAR
push SI;Save registers
push DS
push BP
mov BP,SP ; Set argument pointer
mov AH,0 ; call XMS
call CS:[XMS_Control]
lds SI,[BP+10] ; Get address of pointer
mov [SI],AX ; Store XMS version number
mov [SI+2],BX ; Store driver revision number
mov [SI+4],DX ; Store HMA existence flag
pop BP ; Restore registers
pop DS
pop SI
ret ; Return
_xms_stat ENDP
; Get XMS memory status
; Stores the size of the largest free XMS block and the total
; amount of free XMS memory at the address on the stack
; Borland C++ prototype:
; void far xms_avail(struct XMS_memory_status far*ptr);
; Data Structure:Struct XMS_memory_status
;{
; unsigned short largest_block;
; unsigned short total_free_memory;
; unsigned short error_status;
; };
236页
;Note:the block size and the amount of free memory are in K bytes
ALIGN 16
_xms_avail PROC FAR
push SI ; Save registers
push DS
push BP
mov BP,SP,Set argument pointer
xor BX,BX
mov AH,8; Call XMS
call CS:[XMS_Control]
lds SI,[BP+10] ; Get address of pointer
mov [SI],AX ; Store largest block size
mov [SI+2],DX ; Store total memory available
xor BH,BH
mov [SI+4],BX ; Store error status
pop BP ; Restore registers
pop DS
pop SI
ret ; Return
xms_avail ENDP
; Allocate a block of extended memory
; Returns status in AX: 0001 Success
; 0080 Function not implemented
; 0081 VDISK device detected
; 00A0 All extended memory is allocated
; 00A1 All eXtended memory handles are in use
; Borland C++ prototype:
; unsigned short far xms_alloc(unsigned short size,
; unsigned short far*handle);
ALIGN 16
_xms_alloc PROC FAR
push SI ; Save registers
push DS
push BP ; Make pointer to arguments
mov BP,SP
mov DX,[BP+10] ; Get size in kilobytes
mov AH,9 ; Request memory
call CS:[XMS_Control]
or AX,AX ; Error?
jz__X_a2
; Set handle and return
__X_a1:
lds SI,[BP+12] ; Store handle
mov [SI],DX
pop BP ; Restore registers
pop DS
pop SI
ret ; Return
; Return error
__x_a2:
mov AL,BL ; Get return code
xor DX,DX ; Create NULL handle
jmp __X_a1
237页
_xms_alloc ENDP
; Resize an XMS block
; Returns status in AX: 0001 Success
; 0080 Function not implemented
; 0081 VDISK device detected
; 00A0 All extended memory is allocated
; 00A1 All extended memory handles in use
; 00A2 Invalid handle
; 00AB Block is locked
; Borland C++ prototype:
; unsigned short far xms_realloc(unsigned short handle,
; unsigned short size);
ALIGN 16
_xms_realloc PROC FAR
push BP ;Point to arguments
mov BP,SP
mov DX,[BP+6]; Get handle
mov BX,[BP+8]; Get size
mov AH,0FH
call CS:[XMS_Control]
or AX,AX ; Error?
jz__x_r2
; return
__x_r1:
pop BP ; Restore BP
ret ; Return
; return error
__x_r2:
mov AL,BL ; Get error code
jmp__x_r1
_xms_realloc ENDP
; Lock a block of XMS memory
; Returns status in AX: 0001 Success
; 0080 Function not implemented
; 0081 VDISK device detected
; 00A2 Invalid handle
; 00AC Lock count overflow
; 00AD LOCk failed
; Borland C++ prototype:
; unsigned short far xms_lock(unsigned short handle,
; unSigned long*linear_address);
ALIGN 16
_Xms_lock PROC FAR
push BP ; Save registers
mov BP,SP
push DS
push SI
mov DX,[BP+6];Get handle
mov AH,0CH
238页
or AX,AX ; Error?
jz __x_l2
lds SI,[BP+8] ; Get address of pointer
mov [Si],BX ; Store linear address
mOv [SI+2],DX
; Return
__x_l1:
pop SI ; Restore registers
pop DS
pop SP
ret ; Return
; Return error
__x_l2:
mov AL,BL ; Get error code
jmp __X_l1
_xms_lock ENDP
; Unlock a block of XMS memory
; Returns status in AX: 0001 Success
; 0080 Function not implemented
; 0081 VDISK device detected
; 00A2 Invalid handle
; 00AA BlOCk was nOt lOCked
;Borland C++ prototype:
;unsigned short far xms_unlock(unsigned short handle);
ALIGN 16
_xms_unlock PROC FAR
push BP ; Get pointer to argument
mov BP,SP
mov DX,[BP+6] ; Get handle
mov AH,0DH
call CS:[xms_Control]
or AX,AX ; Error?
jz __X_u2
; Return
__X_u1:
pop BP ; Restore BP
ret ; Return
__x_u2:
mov AL,BL ; Get error code
jmp __X_u1
_xms_unlock ENDP
; Get status of an XMS block
; Returns StatuS in AX: 0001 success
; 0080 Function not implemented
; 0081 VDISK device detected
; 00A2 invalid handle
; Borland C++ Prototype:
; unsigned short far xms_bstat(unsigned short handle,
; struct bstat far*ptr);
; Data structure:struct bstat
239页
; {
; unsigned short lock_count;
; unsigned short free_handle_count;
;};
ALIGN 16
_xms_bstat PROC FAR
push BP ; Save registers
push DS
push SI
mov BP,SP ; Point to arguments
mov DX,[BP+10] ; Get handle
mov AH,0EH
call CS:[XMS_Control]
or AX,AX;Error?
jz __x_b2
lds SI,[BP+12] ; Get pointer to structure
xor DX,DX ; Clear DX
mov DL,BH ; Get lock count
mov [SI],DX ; Store it
mov DL,BL ; Get number of free handles
mov [SI+2],DX ; Store it
; Return
__x_b1:
pop SI ; Restore registers
pop DS
pop BP
ret ; Return
__x_b2:
mov AL,BL ; Get error code
jmp __X_b1
_xms_bstat ENDP
; Move data to or from an extended memory block
; Returns status in AX: 0001 Success
; 0080 Function not implemented
; 0081 VDISK device detected
; 0082 A20 error occurred
; 00A3 Invalid source handle
; 00A4 Invalid source offset
; 00A5 Invalid destination handle
; 00A6 Invalid destination offset
; 00A7 Invalid length
; 00A8 Invalid overlap on move
; 00A9 Parity error occurred
; Borland C++ prototype:
; unsigned short far xms_move(struct XMS_move_data far*ptr);
; Data Structure:Struct XMS_move_data
; {
; unsigned long transfer_size;
; unsigned short source_handle;
; unsigned long source_offset;
; unsigned short destination_handle;
; unsigned long destination_offset;
; };
ALIGN 16
240页
_xms_move PROC FAR
push BP ; Save registers
push DS
push SI
mov BP,SP ; Get argument pointer
lds SI,[BP+10] ; Get address
mov AH,0BH
call CS:[XMS_Control]
or AX,AX ;Error
jz __X_m2
;Return
__x_m1:
pop SI ; Restore registers
pop DS
pop BP
ret ; Return
;Return error
__x_m2:
mov AL,BL ; Get error code
jmp __x_m2
_xms_move ENDP
; Free a block of extended memory
; Returns status in AX: 0001 Success
; 0080 FUnctiOn not implemented
; 0081 VDISK device detected
; 00A2 Invalid handle
; 00A4 Handle is locked
; Borland C++ prototype:
; unsigned short far xms_free(unsigned short handle);
ALIGN 16
_xms_free PROC FAR
push BP ; Make pointer to argument
mov BP,SP
mov DX,[BP+6] ; Get handle
mov AH,0AH ; Free it
call CS:[XMS_Control]
or AX,AX; Error?
jz __x_f2
; Return
__x_f1:
pop BP ; Restore BP
ret ; Return
; Return error
__x_f2:
mov AL,BL; Get error code
jmp __x_f1
xms_free ENdP
; Allocate the HMA
241页
; Returns status in AX: 0001 Success
; 0080 Function not implemented
; 0081 VDISK deviCe detected
; 0090 HMA does nOt exist
; 0091 HMA already in uSe
; 0092 NOt enOUgh byteS reqUested
; Borland C++ prototype:
; unsigned short far hma_alloc(unsigned short size_in_byteS);
ALIGN 16
_hma_alloc PROC FAR
push BP ; Get pointer to arguments
mov BP,SP
mov DX,[BP+6] ; Get number of bytes
mov AH,1 ; Request HMA
call CS:[XMS_Control]
or AX,AX; Failure?
jz __h_a2 ; if so, get return code
;Return
__h_a1:
pop BP ; Restore BP
ret ; Return
; Set error code
__h_a2:
mov AL,BL ; Return error code
jmp __h_a1
_hma_alloc ENDP
; Free the HMA
; Returns status in AX: 0001 Success
; 0080 Function not implemented
; 0081 VDISK device detected
; 0090 HMA does not exist
; 0093 HMA not allocated
; Borland C++ prototype:
; unsigned short far hma_free(void);
ALIGN 16
_hma_free PROC FAR
mov AH,2 ; Release HMA
call CS:[XMS_ContrOl]
or AX,AX; Error?
jz __h_f2 ; If so, get code
; Return
__h_f1:
ret ; Return
; Return error
__h_f2:
mov AL,BL ; Store error code
jmp __h_f1
_hma_free ENDP
242页
; Move data intO Or OUt Of the HMA
; Borland C++ prototype:
; void far hma_move(unsigned short count, unsigned Short direction,
; unsigned short hma_offset, void far*data);
; Note: If the direction is zero, data is copied to HMA;otherwise,
; it is Copied from the HMA
ALIGN 16
_hma_move PROC FAR
push BP ; Get pointer to arguments
mov BP,SP
push DS ; Save registers
push ES
push DI
push SI
mov CX,[BP+6] ; Get byte count
mov AL,[BP+8] ; Get direction flag
Or AL,AL ; If set, copy from HMA
mov AX,0FFFFH ; Set HMA segment
jz __h_m2 ; if clear, COpy to HMA
; Copy data from HMA
mov DS,AX ; Set Source=HMA
mov SI,[BP+10] ; Get offset
add SI,10H ; Add 10H to offset
les DI,[BP+12]; Get destination address
; Do move and return
__h_m1:
cld ; clear direction flag
shr CX,1 ; Convert bytes to words
rep movsw ; Copy data
adc CX.0 ; Add carry
rep movsb ; Copy data
pop SI ; Restore data
pop DI
pop ES
pop DS
pop BP
ret ; Return
; Copy data to HMA
__h_m2:
mov ES,AX ; Set destination=HMA
mov DI,[BP+10] ; Get offset
add Di,10H ; Add 10H to offSet
lds SI,[BP+12] ; Get source address
jmp __h_m1
_hma_move ENDP
; Alter the A20 line status
; Valid argumentS are(GLOBAL_A20_LINE|ENABLE_A20_LINE)==0
; (GLOBAL_A20_LINE|DISABLE_A20_LINE)==1
; (LOCAL_A20_LINE|ENABLE_A20_LINE)==2
; (LOCAL_A20_LINE|DISABLE_A20_LINE)==3
243页
; Returns status in AX: 0001 Success
0080 Function not implented
; 0081 VDISK device detected
; 0082 A20 error occurred
; 0094 A20 line still enabled(args 1 and 3)
; FFFF Illegal argument
; Borland C++ prototype:
; unsigned short far alter_a20(unsigned short change_code);
ALIGN 16
_alter_a20 PROC FAR
push BP ; Get pointer to argument
mov BP,SP
mov AX,[BP+6] ; Get argument
cmp AX,3 ;Check range
ja __a_a2
mov AH,AL ; Create function code
add AH,3 .
call CS:[XMS_Control]
Or AX,AX ; Failure?
jz __a_a3
;Return
__a_a1:
pop BP ; Restore BP
ret ; Return
; Illegal argument
__a_a2:
mov AX,-1; Return ILLEGAL_ARGUMENT
jmp __a_a1
; Bad result
__a_a3:
mov AL,BL ; Return error code
jmp __a_a1
_alter_a20 ENDP
; Get A20 line status
; Returns status in AX: 0000 A20 disabled
; 0001 A20 enabled
; 0080 Function not implemented
; 0081 VDISK device detected
; Borland C++ prototype:
; unsigned short far a20_stat(void);
ALIGN 16
_a20_stat PROC FAR
xor BX,BX
mov AH,7 ; Get line status
call CS:[XMS_Control]
Or BL,BL ; Error?
jnz __a_S2
; Return
__a_s1:
ret
244页
; Return error
__a_s2:
xor AX,AX ; Clear AX
mov AL,BL ; Get error code
jmp __a_s1
_a20_Stat ENDP
; Allocate an upper memory block
; Returns status in AX: 0001 Success
; 0080 Function nOt implemented
; 00B1 A smaller UMB iS available
; 00B1 NO UMBS are available
; Borland C++ prototype:
; unsigned short far umb_alloc(unsigned short size,
; struct umb_data far*ptr);
; Data structure: struct umb_data
;{
; unsigned short segment_number;
; unsigned short segment_size;
;};
ALIGN 16
_umb_alloc PROC FAR
push BP ; Save registers
push DS
push SI
mov BP,SP ; Point to arguments
mov DX,[BP+10]; Get size
mov AH,10H
call CS:[XMS_Control]
or AX,AX ;Error
jz __u_a2
lds SI,[BP+12] ; Get address of data structure
mov [Si],BX ; Store segment number
;Return
__u_a1:
mov [SI+2],DX ; Store size in paragraphs
pop SI ; Restore registers
pop DS
pop BP
ret ; Return
; Return error
__u_a2:
mov AL,BL ; Store error code
ldS SI,[BP+12] ; Point to data structure
jmp __u_a1
umb_alloc ENDP
; Free UMB
; Returns status in AX: 0001 Success
; 0080 Function not implemented
; 00B2 Invalid segment number
; Borland C++ prototype:
; unsigned short far umb_free(unsigned short segment);
ALIGN 16
245页
_umb_free PROC FAR
push BP ;Get pointer tO argument
mov BP,SP ;
mov DX,[BP+6] ;Get segment number
mov AH,011H
call CS:[XMS_Control]
or AX,AX ;Error?
jz _u_f2
;Return
_u_f1:
pop BP ;Restore BP
ret ;Return
;Return error
__u_f2:
mov AL,BL ;Get error code
jmp __u_f1
_umb_free EnDP
XMS_CODE ENDS
END
10.5程序执行
DOS最为有用的功能之一就是它能从一个程序(父程序)中运行另一个程序(子程
序),而不丢失初始程序或父程序的当前状态。在UNIX系统上,函数能运行并行的进程,
DOS也具有类似的功能,只不过当新的进程运行时,父进程会进入睡眠状态(停止执行功
能)。
ExEC功能的一些基本特征是让程序执行以下几项任务:
.在系统的自由内存中运行子程序。
·等待子程序操作完毕。
.接收子程序返回的代码,该代码表示正常完成、出错终止或状态码。
可能读者已听说过EXEC很难使用或用起来很危险。但是,只有事实才最具有说服
力。虽然ExEc的V2版的确有些让人头疼,这点我们稍后讨论(以及如何避免这些问
题),但随后的所有版本都能出色地执行各项指定的功能。尽管执行EXEC功能要求按规
则进行,但这些规则写得清楚明白。
要执行ExEc 功能,必须保证有足够可用的内存来运行新程序(参见本章前面的关
于如何释放内存的内容)。
10.5.1 ExEC功能
只要有足够的内存可供当前程序使用,就可以用ExEC功能来执行另一程序。在调
用EXEC功能(Int 21h,功能4Bh)时,寄存器必须设置成这样:
246页
寄存器 用 途
AL=0 装载和执行程序
DS:DX 指向完整路径名的指针
ES:BX 指向参数块的指针
可将AL置为03h来告诉EXEC功能以覆盖方式装入到父进程已拥有的内存。因为
覆盖不需考虑,因而只需集中精力来执行程序。
DS:DX寄存器对指向可执行文件的路径名。可执行文件的路径名可以是下面这些
名字中的任意一个:
·要执行的程序的文件名,包括.EXE或.COM扩展名(在这种情况下,程序必须处
于当前目录内)。
·与此文件相关的路径名,开头为一圆点(·)并且其对应的目录必须位于程序当前
正使用的路径中。
.此文件的完整路径名。该路径名以根目录(\)开始,且一直延伸到该文件上,其中
还可包括驱动器字母和冒号。
现在,假定想在\UTIL目录中执行DEMO.EXE程序。若已径进入\UTIL目录,则可
按如下方式来使用它:
DEMO.EXE
若当前在\APPL目录中,则可通过与该程序相关的路径名来使用它:
.\..UTIL\DEMO.EXE
在上面这一情况下,可在目录前使用“..”来简化路径名:
..\UTIL\DEMO.EXE
最后,无论当前在哪一位置,都可按下面的格式来执行DEMO.EXE程序:
\UTIL\DEMO.EXE
路径名中必须包括找到该文件所需的一切内容;但其中不能出现通配符。文件名不仅
要清楚、完整,而且还一定要带有.EXE或.COM扩展名。用EXEC功能不能直接执行批
处理文件(带.BAT扩展名),但可用EXEC命令来执行COMMAND.COM,然后把\C选
项开关和该批处理文件的名字作为参数块的部分而传给COMMAND.COM,其结果是批
处理文件得到了执行。
你可能已用惯了某些功能,如文件名中的通配符或该文件的路径搜索,但这些是属于
COMMAND.COM的功能,而不是EXEC的功能。COMMAND.COM通过搜索path(路
径)环境变量来查找文件。它通过使用内部批处理文件的执行过程来执行批处理文件。然
而,所有这些都不能直接用EXEC功能来实现。
EXEC功能的实现原理同样适用于COMMAND.COM内部命令。COMMAND.
COM的内部命令如DIR,不能被单独执行。但是,用EXEC命令来执行COMMAND.
247页
cOM本身却为突破上述限制提供了一种方法。只是有一件事情是这一方式所不允许的,
那就是不允许改变环境;我们会看到用EXEC命令拷贝的每一程序都获取了它自己的环
境拷贝,并且对环境进行改变不可能影响到原始拷贝,这些原始拷贝才是随后一切命令将
用到的东西。
ES : BX寄存器对指向含有下述四种辅助地址的参数块:
·环境块(两字节,段地址)
·命令尾(四字节,偏移值跟段)
·文件控制块1(四字节,先偏移值后跟段)
·文件控制块2(两字节,先偏移值后跟段)
环境块是程序环境变量的集合,这些程序环境变量是由DOS设置的或通过SET指
令以VARIABLE=VALUE格式加进的环境变量。例如,可以把TMP变量设置为等于
TMp——一个暂时的目录区域,创建暂时文件的一些程序将在这一区域中创建文件。
AUTOEXEC.BAT·文件中可能有下面这样的行:
SET TMP=C:\TMP
所有这些变量加在一起就构成了环境块。在大多数情况下,之所以要保存这种环境,
是因为目前的程序要在其中运行。将此环境块置为零会导致EXEC把文件程序的环境拷
贝传递给子程序。但这种拷贝的大小恰恰只可容纳下现有的数据,而没有更多的空间可供
你加入更多的内容(如果想这样试试的话那只能是白费精力,因为当子进程终止时,它的
环境就会被释放父程序看不到它的环境)。
命令尾指针指向跟随在命令行中命令之后的部分。例如,若输入这样的命令:
\BIN\COMMAND/C DIR
the command tail is/C DIR
所保存的命令尾中,包括有指示该命令尾长度的计数字节,命令尾自身以及回车符
(代码(0Dh)。实际使用的完整命令尾应是这个样子:
1 2 3 4 5 6 7 8
06h / C D I R C/R
注意,回车不包括在计数范围内。
后两个参数是程序段前缀(PSP)中文件控制块(FCBs)的初始设置值。除非使用FCB
功能来维持与DOS 1.0版的兼容性,或者让EXEC执行使用FCB的其它程序,否则可以
安全地让它们指向任何地方,因为你用不到它们。如果打算使用FCB,则必须像第9章“目
录和文件”中介绍的那样来设置它们。可以通过EXEC把FCB复制到程序段前缀(PSP)。
列表10.9的程序示出前面提及的COMMAND.COM的用法;本程序执行带有/C
DIR命令尾的COMMAND.COM。请注意如下的特性:
·程序中没有明显地保存堆栈指针,虽然该指针被破坏,但intdos会自动地恢复这
一指针。
·必须获取段寄存器值,以便正确地设置所有的指针。
248页
我们的程序只是简单地假定COMMAND.COM位于当前驱动器的根目录下;我
们建议,为了使程序更通用,应该再加进一段代码,搜索环境,找到COMSPEC=
串,然后拷贝跟在等号后面的所有字符。这样,如果当前驱动器不包含COM-
MAND.COM,该程序也能运行。
列表10.9
/* exectest.c
Listing 10.9 Of DOS Programmer's Reference*/
#include<stdio.h>
#inClude<dOS.h>
#inClude<String.h>
void main()
{
union REGS regs;
struct SREGS segs;
struct {
int envblk;/*Environment block*/
int ocmd;/*Command tail*/
int scmd;
int ofcb;/*FCB #1*/
int sfcb;
int ofcb2; /*FCB #2*/
int SfCb2;
}pblock;
char buffer[256];
char name[128];
segread(&segs);
strcpy(name,"\\command.com");
printf("Executing program %s\n",name);
/*Set up the command tail*/
buffer[0]=6; /*Number of characters*/
strcpy(buffer+1,"/c dir\015");/*Command tail*/
pblock.ocmd=(int)buffer;
pblock.scmd=segS.ds;
/*USe parent's environment*/
pblock.envblk=0;
/*Set up FCBs #1 and #2*/
pblock.ofcb=(int)buffer;
pblock.sfcb=segs.ds;
pblock.ofcb2=(int)buffer;
pblock.sfcb2=segs.ds;
/*Execute the designated program using the
EXEC function 4Bh*/
regs.h.ah=0x4b;
regs.h.al=0;
regs.x.dX=(int)name;
segs.es=segs.ds;
regs.x.bx=(int)&pblock;
intdosx(®s,®s,&segs);
/*If the carry flag is set,an error code is
in the AX register*/
if(regs.x.cflag==1)
249页
printf("Error Code=%d\n",regs.x.ax);
}
程序在知道了段寄存器值以后,它就会设置用于EXEC功能调用的每一个参数、命
令尾、环境(缺省值)和FCB。然后,程序执行各种功能,并检查是否有返回的错误代码。
在C语言中,有一个很好的能用于运行子进程的exec()函数,几乎可用于所有的子
进程运行场合。 Turbo Pascal也提供了这样的exec()函数,但用法略有不同。前面曾提到
过,BASIC和Turbo Pascal 4.0版以前的版本都不具备这些函数来运行子进程。
10.5.2程序退出
DOS EXEC的强大功能之一就是给它的父进程传回一个表明状态的代码。通过使用
INT 21h的功能4ch(终止功能),程序就能传回一个退出状态,以备将来作出决定时的条
件码。 uNIX系统一直使用这一功能来管理程序的系统执行。
例如,最近有些程序通过一连串的操作来管理一系列其他程序的执行情况。根据程序
的结束方式,主程序提供相应的分支程序来改变进程。在这方面,最复杂的莫过于通过接
口与IBM主机连接,以及如何对数据库作智能决策。若IBM主机数据传送行为并不发
生,就可跳过“把记录录入数据库”这一步,而直接使用数据库。
程序的返回代码在批处理文件内可做为ERRORLEVEL变量使用,从而在该文件中
进行逻辑分支。若从一个程序(父程序)中执行另一个程序(子程序),那么在EXec功能
返回之后,父程序就可以调用Int 21h,功能4Dh来获取子程序的返回代码。寄存器AH中
有如下几种退出类型:
退出代码 含义
00 正常终止
01 由Ctrl-C命令终止
02 关键设备出错
03 TSR返回(Int 21h,功能31h)
寄存器AL中含有子进程的返回代码
10.5.3 潜在的ExEC问题
本章前面曾提到,DOS 2.0版中的EXEC存在一些潜在的问题,并答应在后面为读
者提供一些避免这类问题的方法。本小节介绍的就是EXEC中的问题以及克服这些问题
的方法。
只有使用汇编语言时,EXEC中的问题才较明显,作为EXEC的常规DOS接口的一
部分,高级语言中似乎未出现这些问题。那么,这些问题的根源究竟在哪儿?看看下面这
个事实相信我们能找出答案。2.0版本中执行EXEC功能的DOs例行程序是用块移动指
令来把全部数据块传送到它自己的工作环境中。但块移动是由CPU芯片中的“方向”标志
控制的。若设置此标志,它会假设要传送的数据块位于所有块的末端,因而将末端的数据
250页
块移走。若清除此标志,则从开头部分开始传送数据块。这样,前后传送的数据块就会不
同。
EXEC功能允许清除此标志,但在标志清除后却不采取任何安全措施。因而,若在调
用EXEC时设置这种方向标志,则接下来所发生的一切是DOS设计者们根本不愿看到的
内容。最常见的情形就是系统被直接锁定。因为它此时正在把一套二进制机器指令当作
文件名来使用,这样,它会去装载并执行一个实际上根本不存在的文件。
在3.0版本中,可在EXEC代码的前面加上一种CLD指令,以确保正确的清除该方
向标志,因此避免了此类问题的发生。要使2.0版本中不至出现这种问题,可用Int 21h调
用EXEC功能之前,人为地加上CLD指令。
在所有已公开的DOS版本中,还存在着一个潜在的问题,即除CS和IP之外,整个调
用期间没有保存其他的寄存器。这意味着在返回时,只能把SS和SP保存在能用CS定址
的位置,或者换句话说,用户将失去对程序的控制。
10.5.4往DOS中输入命令
往DOS中输入命令时,DOS首先将命令名与命令参数分开。随后,DOS检查此命令
是不是一条内部命令。用于执行内部命令(如CLS或DIR)的代码位于DOS命令行解释
程序内。由于首先检查的是内部命令,因此用户自己所写的命令不能是DIR,也不能在命
令行上执行它。但是,有些办法可让用户添加DOS内部命令,或者将DOS内部命令替换
掉,具体情况要看使用的是哪一个DOS版本。
现在假定找到的不是一条DOS内部命令,随后就会搜索磁盘系统,期望找到相应的
文件。在当前工作目录中搜索文件名时,顺序是找具有.COM、.EXE及BAT扩展名的文
件。如果在当前目录中找不到相匹配的文件,就会继续搜索PATH字符串中环境变量中
的每一个目录。若搜遍PATH后,仍未找到匹配的文件名,则显示出“Bad filename or com-
mand"(错误的文件名或命令)信息;并且DOS会返回到提示符处。
若找到批处理文件(具有.BAT扩展名),则授权给DOS批处理文件解释程序来处理
该文件。否则,调用DOS EXEC功能(功能043H,子功能000H)。若此文件的头两个字节
为MZ,就把此文件当作EXE型文件,在而不管后跟的扩展名是什么。因此,可以保证,用
COM扩展名来重命名该文件时,就可装载一个EXE文件,随后创建新的程序段前缀,将
这种COM文件逐字逐句地复制到内存中,把段寄存器设置在内存(程序就是在这里写成
的)中的节上,并且把控制权交给CS:00100H。而发生在EXE文件上的一切则更复杂
(参见图10.4)。
由于使用COM文件,创建了新的PSP,因而,除EXE文件头外,可将整个EXE文件
装入内存中位于PSP之上的位置,我们称此位置为装载地址。随后,把DS和ES寄存器
设置成指向此PSP,通过EXE头的值来设置CS:IP和SS:SP寄存器,并把装载地址处
的段值放入CS和SS寄存器中。
最后,用EXE头中的重定位表修改内存映象中的段引用。重定位表由段:偏移值指
针组成。在这里,指针就是内存映象中的字的地址,该地址是相对于装载地址的。程序的
段值依次指向内存映象中的每个字,被指向的字为加进的装载地址段相应的段值。采用这
251页
图10.4 EXE文件头
种方式,可调整程序的段引用以平衡内存(装载程序的地方)中的地址。
10.5.5替换DOS的内部命令
替换DOS的内部命令可采用三种主要方法。第一种方法适用于DOS的全部版本,即
直接用DEBUG或第三方厂家的文件编辑程序来修改COMMAND.COM,并改变修改后
的命令的名称。这种方法并不难,因为很容易找到COMMAND.COM中的命令名列表。
把初始内部命令名中某个字母换成另一大写字母,如把COPY换成KOPY,就能保留与
此名字对应的应用程序。若想取消此命令,只需把命令名中某个字母改成小写就行了,如
果把COPY改为cOPY。此cOPY决不会与敲入的命令相匹配。
第二种方法适用于DOS3.0及更高的版本,先为命令名环境(name-alike)规定出完
整的路径,例如:
C:\DOSCLONE\COPY*.*
252页
然后,调用COPY(拷贝)版本。
第三种方法适用于DOS3.3及更高的版本,这种方法就是创建一种TSR,此TSR链
接DOS Interrupt(中断)2F,并截获功能0AEH,子功能000H和001H。DOS在检查它自己
的内部命令列表前调用这些中断功能。
把DS和ES设置为指向DOS的暂驻存储区,BX指向含有两个计数字节和一个DOS
命令行的缓冲区,并且让SI指向一个含有命令行(已转换成大写字母,且未超出字符计
数)中命令字的缓冲区,这样便可发出第一个调用(调用功能0AEH,子功能000H)。若命
令(或命令之一)刚好与Si缓冲区匹配,则此命令必须把AL置为0FFH,并随即返回。否
则,它就会链接到前面的中断处理程序上。
在第二个调用里(调用功能0AEH,子功能001H),寄存器的设置与第一个调用相同,
只是暂时改变BX所指的数据。这个主意不错,因为这样便可在调用子功能000H时,把命
令行参数写入内部缓冲区。执行完代码后,必须清除DS:SI命令缓冲区中的字符计数。
以便让DOS知道已执行过此代码。
对于所执行的代码,唯一要做的事就是清除字符计数。尽管对代码来说,这样作毫无
用处,但DOS满意,因为对DOS来说,有用的工作已完成。用这种方法来消除出故障的
DOS内部命令如DEL或ERASE,使用起来很简便。
10.5.6为什么有些EXE文件不能被转换成COM文件
若EXE文件不符合以下几条准则,EXEZBIN就不把EXE文件转换成COM文件:
·EXE文件必须在其文件头上带有MZ标志。
·在文件头中,SS:SP引用值必须为0000:0000。
·CS:IP引用值只能是0000:0100或0000:0000(用于设备驱动程序)。
·重定位表中的项数必须为零。
10.5.7程序段前缀(PSP)
程序段前缀是一个有256字节的数据块,DOS就是用其中的数据来构造它所运行的
每一个程序的。PSP除含有DOS必须维持的信息外,还含有DOS程序要使用的信息。表
10.1显示了PSP的结构。
位于PSP:0000和PSP:0005处的代码是与CP/M兼容的早期DOS版本遗留下来
的;它们的作用与cp/M CALL0000和CALL0005相同。
表10.1 PSP结构
偏移值 内容 含义
000H-001H CD20 调用DOS终止例程
002H-003H 可变的 位于分配给程序的内存块顶部的段地址
004H 00 填充程序
005H-009H 可变的 对Int 21H调度程序的远程调用
00AH-00DH 可变的 Int 23H向量
253页
(续)
偏移值 内容 含义
00EH-011H 可变的 Int 24H向量
012H-015H 可变的 Int 22H向量
016H-017H 可变的 父进程PSP的段
018H-028H 01 01 01 00 02 系统文件表的索引
FFFFFFFFFF
FFFFFFFFFF
FFFFFFFFFF
02CH-02DH 可变的 环境拷贝的段地址
02EH-031H 00 00 00 00 SS:SP存储器
032H-033H 14 00 可用的文件句柄数
034H-037H 可变的 指向文件句柄表的指针
038H-03BH 可变的 SHARE的前一个PSP
03CH-04FH 00 00 00 00 00 未使用
00 00 00 00 00
00 00 00 00 00
00 00 00 00 00
050H-052H CD 21 CB 对Int 21H和RETF的调用
053H-053H 00 00 00 00 00 未使用
00 00 00 00
05CH-06BH 可变的 缺省FCB1
06CH-07FH 可变的 缺省FCB2
080H 可变的 命令尾计数
081H-0FFH 可变的 命令尾
程序可用PSP:0002处的段地址来确定分配给它的内存是否足够。
位于PSP:000A、PSP:000E以及PSP:0012处的向量地址是MS-DOS在退出时
恢复的地址。程序得到的警告是不要修改低于PSP:00TC的值,但可以修改psP:000E
和PSP:0012(分别代表Ctrl-C和关键出错处理程序向量),而且不会造成危害。psp:
000A处的向量实际上是紧跟在DOS EXEC调用之后的父进程的返回地址,可将地址调
整为指向用户定义的过程,这一过程可与程序已安装的一切特殊处理程序脱钩。然后,此
过程必须返回到位于PSP:000A处的初始向量处。只要调用用户定义的过程,就会释放
所有的程序内存,关闭文件,并且可把PSP调整为指向父进程的pSp。在此过程中,无文
件访问或其他I/O行为发生。
父进程PSP可用来往回跟踪链接父COMMAND.COM的PSP,而父COMMAND.
COM的PSp则把它自身显示为自己的父进程。这种COMMAND.COM不能是主COM-
MAND.COM;它可以是外壳程序(shell)。
可用系统文件表的索引来打开stdin、stdout、stderr、stdaux和stdprn的句柄,用值FF
来关闭这些句柄。可改变PSP:0032处的值和PSP:0034处的指针来反映不同的句柄
表PSP:0034处的值则被初始化为PSP:0018。
254页
可用环境段地址来访问程序的环境拷贝。可以往回跟踪父进程PSP,以访问并修改父
进程的环境。而且,可以肯定,这一环境不是主环境。
DOS用PSP:002E处的SS:SP存储器来存储程序在Int 21H入口处的SS:SP。这
一功能允许DOS执行多重任务,但此功能至今仍未正式使用。
当SHARE(共享)在DOS 3.3及以后的版本中投入使用时,就可用PSP:0038H的
指针来保存父进程的PSP。在DOS 3.3以前,往往把PSP:0038H处的指针,设置为指向
FFFF:FFFF。
PSP:0050H处代码提供了另一种机制来访问Int 21H,这一机制使用的是了种远程
调用。
FCB可供使用FCB式文件I/O的程序使用。假设命令尾中的参数是文件名,那么,可
用命令尾中的头两个参数来装载FCB。这种装载方法是从CP/M时代继承下来的。
命令尾计数就是命令尾的长度。命令尾是命令的剩余物,从命令名后面一直延伸到处
于末端的回车,这段代码长度都属于命令尾。命令尾计数未把回车代码计算在内。若此命
令无参数,则命令尾计数为0,并且其回车代码(0DH)处于PSP:0081处。
DOS将命令尾和命令尾计数设置成缺省磁盘传送区(DTA)。这一功能也是从CP/M
中继承下来的,若使用FCB文件功能,则必须使用这一功能。
10.6内存常驻软件的编程
TSR(内存常驻软件)实用程序是一个很热很热的项,被大量地用于PC类编程。自
Borland的SideKick问世后,PC软件市场就一片热闹。有那么多的TSR实用程序可用,但
若将它们全部都用起来,就没有更多的内存空间来运行可执行的程序。人们似乎在不断地
想要很多很多的TSR,而不管他的机器实际上能否容纳得下这么多。
大多数人并未意识到他们眼中的TSR实质上可分为两种能设置和使用的TSR。TSR
实用程序(像SideKick和其他的实用程序一样),并不是TSR功能的真正用途。因为最初,
人们只是把TSR看作是操作系统的扩展。
有如下两种能设置和使用的TSR:
·活动的TSR这些弹出式实用程序如SideKick,是TSR中最普遍使用的类型。它
们通常申相应的特定按键——人们称之为热键的方法来操作。激活这些实用程序
后,它们会取代计算机。并且在把控制权归还给最初控制此机器的程序之前,执行
它们的功能。
·不活动的TSR当调用程序调用指定的中断时,这一类TSR会作出应答,当调用
这类TSR时,它们将执行已定义好的功能,然后,类似于子程序,把控制权还给调
用程序。
活动的TSR给用户造成一种印象,即在PC机上可进行多任务执行。实际上,TSR并
不执行多任务,但是用户就是得到这种印象,好像不止一种操作在同时进行。要解释这种
现象只能说TSR较复杂。正如读者知道的那样,DOS是不可重入的(不可能中断正在进
255页
行中的内部DOS程序并从别的地方来重启动它)。当使用TSR往磁盘中写入某一内容
时,若DOS也正在处理某一操作(例如,磁盘访问),那么,将引起严重的问题,并可能把磁
盘弄得乱七八糟。
活动的TSR必须监视DOS和用户双方的行为。在第11章的描述中,读者会看到采
用的策略就是把TSR作为中断服务程序。
不活动的TSR在一种温和的环境下工作。在程序调用它之前,这种不活动的TsR并
不做任何事情。由于DOS是一种单任务的环境,在处理过程中,一次只能进行一种操作,
并且我们知道,调用TSR时,无DOs操作行为发生。因而可以安全地从不活动的TSR中
使用DOS调用。
自从DOs问世以来,TSR的概念经历了几次重大的改变。最初的TSR 功能(Int
27h)已被Int 21h的功能31h所取代。这种Int21h的功能31h用起来更方便,因为它允许
传递返回代码,并让TSR 使用64K以上的内存。这两方面的因素使得人们更喜欢使用后
一种TSR调用:Int 21h的功能31h。
当TSR运行时,TSR先设置它自己的内存表,并作好连接DOS中断的准备。一切准
备就绪后,TSR程序确定需要保留多少内存;然后把AH置为31h,AL置为返回代码,并
把DX置为分配给TSR的节(16字节的块)数。程序退出时,会把用于执行程序的内存数
量减少到TSR指定的数量,并且把退出代码返回到父程序。
听起来很简单,不是吗?对非常简单的TSR来说可能是这样。但是,若继续去编写类
似于sideKiek一类的程序时,就会发现事情远远不只是执行TSR那样简单。
首先,必须找到某种方法来激活TSR的行为。可以为TSR加上计时器中断,并且每
隔一个规定的秒数,就去激活一次TSR的操作。更常见的情况是,把TSR与键盘中断服
务例程结合在一起,并找到一种固定的击键。由于TSR的猛增,不可避免地会出现一些冲
突,因为大多数击键可用于好几个地方。
无论采用何种方式一把TSR加给某个系统,TSR都必须意识到有这种可能,即此系
统内还有其它的TSR出现。要允许同一触发器上出现其它的操作,TSR必须调用中断服
务例程,该例程在TSR启动以前处理中断。更进一步地讲,要防止与其它功能重叠,必须
能改变触发TSR的击键。
处理DOS功能时,TSR和中断服务例程(ISR)同样都有危险。 MS-DOS系统一直只
容许单用户、单任务的系统,因此是不可重入的。由于TSR或ISR对应的事件与DOS功
能的操作有可能不同步,因此在DOS内部功能的行使过程中,TSR或ISR就有可能控制
计算机。有几种未公开的DOS 功能可帮助你确定何时使DOS功能是安全的,以及何时不
能安全使用。这些未公开的DOS功能在第11章“中断处理程序”中介绍。
编写TSR是一个范围广泛的话题,足以写一整本书。复杂的TSR可能涉及到许多予
功能、窗口,以及其它一些足以让众多高级程序员用很长时间才能实现的功能,因此,这里
只能将它们略去。如果你已从11章里学到足以能使用中断的时候,你就有能力构造简单
的TSR。
256页
10.7小 结
本章已介绍了内存管理、扩展内存和程序执行等知识。从中可掌握用DOS分配TPA
(传送程序区)来满足那些需要内存的程序的请求。程序启动时,它们通常获取运行时要用
到的全部内存,并且必须释放所有不使用的内存。
使用汇编语言的程序员必须明确地释放内存。而那些使用C语言或Turbo Pascal最
新版本的程序员们则可通过使用程序的启动代码来完成这一工作。除了编译期间以外,其
余的时间里BASIC和其它的Pascal程序员均不能释放内存。
扩展内存允许通过把大量的内存分页装入1M的PC可寻址内存来访问附加内存。
可用16K页中的内存来保存和获取数据。要做到这一点,必须使用Int 67h功能调用,在
本书的末尾对“EMS参考手册”的讨论中详细地谈到了此功能调用。
扩充内存则允许通过使用80286、80386和80486的功能来寻址超过1M的PC常规
内存,从而达到访问附加内存的目的。可以使用任意倍数的1K内存来保存和获取数据。
可使用XMS功能调用来实现这一点,有关XMS功能调用的详细情况可见本书后面
“XMS参考手册”一节的内容。
一旦拥有足够可用的内存,DOS便允许从其它程序中运行程序(COMMAND.COM
就是这么做的)。可以用这些内存来把各个单独的程序连接成软件系统。TSR(终止并驻留
程序)允许在程序的操作过程中,在同一内存里创建其它程序。使用第11章“中断处理程
序"中介绍的技术可创建TSR,从键盘中调用TSR能给人以一种多任务操作的映象。
(未完待续)