中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --

联盟域名:www.cn-dos.net 论坛域名:www.cn-dos.net/forum
DOS,代表着自由开放与发展,我们努力起来,学习FreeDOS和Linux的自由开放与GNU精神,共同创造和发展美好的自由与GNU GPL世界吧!

中国DOS联盟论坛
现在时间是 2026-09-15 00:27
中国DOS联盟论坛 » DOS开发编程 & 发展交流 (开发室) » dos下超出常规内存的编程 查看 2,010 回复 1
楼 主 dos下超出常规内存的编程 发表于 2010-11-28 20:33 ·  中国 陕西 西安 电信
新手上路
积分 8
发帖 3
注册 2010-11-24 22:44
15年会员
UID 178213
性别 男
状态 离线
有一个程序,是borland pascal 7.0 for dos 写的。现在要给里面加一些功能,需要定义一个大的数组,这个数组大概占100K吧。可能之前的程序都快接近DOS常用规内存极限了。现在加个这个数组,总提示数据段超出。看帮助文档,说是可以以开僻内存的形式(malloc),发现运行后出现heap overflow. 是不是得用保护模式编程??或者有什么办法可以让我定义大的数组??谢谢各位了。
[已使用 Gemini 解答]
Gemini 解答

1. 错误原因与技术背景分析

在 16 位实模式 DOS 下,Borland Pascal 7.0(BP7)遇到该问题是由 x86 实模式的分段内存架构以及 DOS 常规内存限制共同导致的:

  • 数据段超出(Data segment too large): 在实模式下,BP7 的所有全局变量共享同一个默认数据段(DS),其硬件寻址上限为 64KB($2^{16}$ 字节)。因此,全局静态变量的总和绝对不能超过 64KB,无法直接在全局声明一个 100KB 的数组。
  • 堆溢出(Heap overflow / Runtime Error 203): DOS 实模式的全部寻址空间仅有 1MB,其中常规内存(Conventional Memory)上限为 640KB。扣除 DOS 内核、驱动程序(TSR)、程序代码段以及全局数据段后,留给堆(Heap)的连续常规内存可能已不足 100KB。此外,BP7 实模式下的单次动态内存分配(GetMem)受 16 位指针限制,单块连续内存最大不能超过 65,521 字节($64\text{KB} - 16\text{ 字节}$),请求分配 100KB 会直接导致堆管理器报错。

2. 解决方案与实现路径

要处理超出 64KB 乃至超出常规内存的数据,主要有以下三种工程途径:

方案一:切换到 BP7 保护模式目标(推荐)

Borland Pascal 7.0 原生支持 16 位 DPMI(DOS Protected Mode Interface)保护模式编译,这是改动现有代码最小的方案:

  1. 编译目标切换:在 IDE 的 Options -> Target 中将目标平台从 Real 改为 Protected(命令行使用 bpc /CP)。
  2. 突破常规内存:保护模式程序直接运行于保护模式下,通过 DPMI 扩展器(运行时依赖 RTM.EXEDPMI16BI.OVL)访问最高 16MB 的扩展内存(Extended Memory),堆空间不再受 640KB 常规内存限制。
  3. 突破 64KB 单块限制:在 DPMI 模式下,BP7 允许分配大于 64KB 的动态内存块(使用 GlobalAllocPtr 等 DPMI API 或指针数组分块映射)。

方案二:实模式下调用 XMS / EMS 驱动

如果程序必须保持在 16 位实模式下运行,可以通过扩展内存驱动管理数据:

  • XMS(Extended Memory Specification):依赖 HIMEM.SYS,通过 INT 2Fh(AX=4310h 获取驱动入口)调用 XMS 功能号。通过 XMS 分配 100KB 的扩展内存句柄(Handle),在需要读写数据时,调用 XMS 0Bh 功能(Move Extended Memory Block)在常规内存缓冲区与扩展内存之间批量复制数据。
  • EMS(Expanded Memory Specification):依赖 EMM386.EXE,通过 INT 67h 管理 16KB 页框映射。

注:此方案需要重构数组的访问逻辑,将直接的下标寻址改为“按需换入/换出缓冲区”的操作。

方案三:数据分块与指针数组(仅当剩余常规内存 > 100KB 时有效)

若实模式下剩余常规内存总量足够,只是受限于单块 64KB 上限,可将 100KB 数组拆分为多个小于 64KB 的子块:

type
  TBlock = array[0..51199] of Byte; { 50KB }
  PBlock = ^TBlock;

var
  Part1, Part2: PBlock;
begin
  GetMem(Part1, 51200);
  GetMem(Part2, 51200);
  { 通过逻辑封装访问:Index < 51200 访问 Part1^,否则访问 Part2^ }
  FreeMem(Part1, 51200);
  FreeMem(Part2, 51200);
end;

3. 补充说明:回帖中提及的 CRT 溢出问题

回帖中提到的“高于 200MHz CPU 报除 0 错误”,是 Borland Pascal 7.0 / Turbo Pascal 7.0 的经典已知缺陷:

  • 故障机制:程序启动时,CRT 单元会执行一段基准循环以校准 Delay 函数所需的计数器(DelayCnt)。在高主频 CPU(通常超过 200MHz)上,该循环在规定时间内执行的次数过大,导致在执行 16 位除法指令(DIV)时发生商溢出,硬件触发 INT 0 中断,表现为启动即崩溃并提示 Runtime error 200 at xxxx:xxxx
  • 解决方法:无需降级编译器,可通过替换修复后的开源 CRT.PAS 重新编译程序,或使用社区公认的二进制补丁工具修复已编译的 EXE 文件。
2 发表于 2010-12-04 18:36 ·  中国 福建 厦门 电信
高级用户
★★★
积分 741
发帖 366
注册 2007-07-25 19:11
19年会员
UID 94024
性别 男
状态 离线
1、即便是Huge模式,单项静态数据也不得超过64K,所以最好用汇编编写一个居于xms接口来把数据放到xms里面,把汇编后的obj连接进来以供Pascal使用。
2、Turbo Pascal 7/7.01的CRT.ASM模块有严重问题,在高于200M的CPU上很容易出现除0溢出,严重不推荐此版本,如果硬要使用,要修改CRT.ASM并重新生成CRT库。
论坛跳转: